سایت یک مرکز مشاوره معمولاً دو کار را بد انجام میدهد: از بازدیدکننده فرم پر میکند، و مجبورش میکند تلفن بزند تا بپرسد کسی وقت خالی دارد یا نه. هر دو کندند و هر دو آدمها را از دست میدهند.
میخواستم بدانم جایگزینی برای این با یک حباب چت تعبیهشده که خودش تریاژ را انجام میدهد چه هزینهای دارد. نتیجه، ویجت مشاورهٔ هوشمند است: یک دستیار مصاحبهٔ فارسی و راستچین که کلینیک با یک تگ <script> روی سایتش میگذارد.
محدودیتی که همهچیز را شکل داد
تصمیم مهم، رابط کاربری نبود. آنچه پشت آن اجرا میشود بود.
اینجا هیچ مدل زبانی ابری وجود ندارد. «هوش مصنوعی» یک موتور محلی و قطعی است: یک واژگان بالینی پالایششده که متن آزاد را به مفاهیم نگاشت میکند، تطبیق فازی نام مشاور، تجزیهٔ بازهٔ سنی، امتیازدهی وزنی روی فهرست مشاوران، و یک جدول بازخورد که وزنهای آینده را بازتنظیم میکند. حرف بازدیدکننده هرگز از سرور خارج نمیشود، هیچ کلید API فروشندهای در کار نیست، و هزینهٔ هر پیام صفر است.
این تصمیم سه چیز را برای ابزار سلامت روانی ممکن میکند: یک پردازشگر دادهٔ شخص ثالث را از مسار افشای حساس حذف میکند، هزینهٔ هر پیام را صفر میکند — که برای کلینیکی با بودجهٔ کوچک تعیینکننده است — و رفتار سیستم را قابل حسابرسی میکند: هر پیشنهاد با یک دلیل صریح میآید، نه با جملهای که فقط خوشآهنگ به نظر میرسد.
بازدیدکننده دقیقاً چه میگیرد
یک حباب شناور که پنلی ۳۸۰×۵۸۰ باز میکند، و زیر ۴۸۰ پیکسل تمامصفحه میشود. گفتگو یک ماشین حالت روی گامهای شمارهدار است — text، phone، number یا choice — با گامهای شرطی اختیاری و دو پایان ممکن. هیچکدام از اینها ثابت کدنویسی نشده است.
خودِ گامها از پنل مدیریت کلینیک زنده ویرایش میشوند: مرکز میتواند سؤالهای مصاحبه را عوض کند، ترتیبشان را جابهجا کند، یک شاخه اضافه کند، یا پایان جریان را از «پیشنهاد مشاور» به «ثبت لید» تغییر دهد — بدون redeploy. هر tenant میتواند دامنههای مجاز برای embed شدن ویجت را هم فهرست کند؛ این بررسی تطبیق دقیق host یا subdomain است، عمداً نه تطبیق زیررشتهای، تا notexample.org نتواند خود را جای example.org جا بزند.
دو قاعدهٔ اعتبارسنجی ارزش گفتن دارند، چون همین جزئیات تعیین میکند فرم حساس به نظر برسد یا نه. شمارهٔ تماس باید دقیقاً ۱۱ رقم و با 09 شروع شود، و ارقام فارسی پیش از بررسی تبدیل میشوند — یعنی بازدیدکنندهای که ۰۹ مینویسد، بهخاطر نوشتن با خط خودش رد نمیشود. شمارهٔ مشکوک فقط هشدار است نه رد: مصاحبه ادامه پیدا میکند، چون شمارهٔ بلاکشده یعنی کسی که کمک لازم دارد کمکی نگرفت.
مرحلهٔ غربال اختیاری است، و غربال است نه تشخیص
اگر tenant آن را فعال کند باشد، ویجت پرسشنامهٔ سلامت عمومی GHQ-28 را پیشنهاد میدهد: ۲۸ سؤال در چهار زیرمقیاس — جسمانی، اضطراب، اجتماعی و افسردگی. پاسخها سمت سرور نمرهگذاری و به بازه تفسیر میشوند؛ هشدار اصلی در نمرهٔ کل ۴۳ به بالا یا زیرمقیاس افسردگی ۱۵ به بالا، و هشدار ملایمتر از نمرهٔ کل ۲۲.
تمایزی که کد روی آن اصرار دارد این است که این یک ابزار غربال است، نه تشخیص. ویجت همینطور ارائهاش میکند و موتور تطبیق هم همینطور: هرگز به بازدیدکننده نمیگوید چه اختلالی دارد. مفهوم استخراج میکند، امتیاز میدهد، رتبهبندی میکند و توضیح میدهد.
رتبهبندی دو مشاور از میان یک فهرست
موتور تطبیق اول فیلترهای سخت را اعمال میکند — جنسیت، شعبه، بازهٔ سنی و محافظت تخصص حقوقی — و بعد آنچه باقی مانده را روی تناسب موضوع، مفاهیم بالینی استخراجشده، توانمندی، تعداد نوبت خالی زنده و وزن یادگرفتهشدهٔ هر مشاور امتیاز میدهد. خروجی، دو مشاور برتر است، هرکدام با دلیلی به زبان ساده و لینک رزرو.
وزن یادگرفتهشده همان بخشی است که این را از دمو به محصول تبدیل میکند. هر امتیازدهی در پنل و هر توصیهٔ موفق، وزنِ (tenant، مشاور، مفهوم) را در یک بازهٔ بسته تنظیم میکند. یعنی کارکنان خود کلینیک، با امتیاز دادن به یک پیشنهاد، موتور تطبیق را دوباره آموزش میدهند.
در مرکز این کار یازده مفهوم بالینی قرار دارد: اضطراب، افسردگی، وسواس، روابط و تعارض بینفردی، زوج و ازدواج، والد و فرزند، کودک، نوجوان و جوان، توسعهٔ فردی، حقوقی، و پزشکی-روانپزشکی. واژگان مترادف پالایششده و خوانا است و هر مشارکتکنندهای میتواند آن را گسترش دهد.
لید پیش از اجرای تطبیق نوشته میشود
این تغییر کوچک اما پیامد بزرگی دارد. درخواست پیش از اجرای تطبیق ذخیره میشود، پس کسی که به مشاور نیاز دارد حتی وقتی موتور هیچ تطبیقی پیدا نمیکند هم ثبت میشود. رفتار قبلی آنها را دور میریخت، و لیدِ افتاده یعنی کسی که کمک خواست و جواب نگرفت.
سمت کلینیک
پنل مدیریت نیمهٔ دیگر محصول است: داشبورد، صندوق کامل گفتگوها، جدول لیدها، صفحهٔ حل تعارض، ویرایشگر جریان، مدیر فهرست مشاوران، بازخورد، کنترل خزنده، و بخش super-admin برای مدیریت tenantها. لیدها به یک فایل Excel دو شیتی صادر میشوند — Users و Request history — با سربرگهای ثابت و قالببندی فارسی.
تازگی نوبتها را یک خزندهٔ پسزمینه تأمین میکند که هر دو ساعت صفحات عمومی تیم مرکز را میخواند، با retry، backoff و fallback اتصال مستقیم. لینک رزرو برای هر مشاور نگاشت میشود و تعداد slot مستقیماً وارد رتبهبندی میشود، پس موتور کسی را ترجیح میدهد که بازدیدکننده واقعاً میتواند رزرو کند.
امنیت چطور است
رمزهای عبور با PBKDF2-HMAC-SHA256 و ۱۰۰٬۰۰۰ تکرار و salt شانزدهبایتی هش میشوند و به شکل salt$hex_digest ذخیره میشوند. نشستها کوکیهای امضاشده با httponly و samesite=lax هستند. پاسخها nosniff، strict-origin-when-cross-origin و X-Frame-Options: SAMEORIGIN میفرستند — بهجز /widget که باید قابل embed باشد.
محدودسازی نرخ واقعی است: ۳۰ درخواست در دقیقه بهازای هر IP کلاینت و توکن نشست روی API چت، و ۱۰ تلاش ناموفق ورود به پنل در هر ۵ دقیقه بهازای هر IP. کلید API مربوط به tenant خودش هویت tenant است — جریان، فهرست مشاوران، وزنهای یادگیری و لیدها را محدود میکند — و از پنل super-admin قابل چرخش است، و ویجت کلید جدید را در بارگذاری بعدی برمیدارد.
پشتهٔ فنی، و یک قانون غیرقابل مذاکره
FastAPI 0.115.6 با uvicorn 0.34.0، Jinja2 برای پنل، SQLite برای ذخیرهسازی، APScheduler برای خزنده و GC نشستها، و جاوااسکریپت و CSS خالص برای خود ویجت — بدون فریمورک، بدون باندلر، بدون npm. ویجت در قالب سه فایل استاتیک تحویل میشود.
قانونی که قابل مذاکره نیست: یک worker. jobهای زمانبندی داخل همان پروسهاند و شمارندههای محدودسازی نرخ در حافظه. چند worker یعنی چند خزنده و شمارندههای تقسیمشده. تا وقتی زمانبند از پروسه بیرون نرود، عمودی مقیاس بدهید.
قدم بعدی چه باشد
قدم بعدیِ صادقانه مدل بزرگتر نیست. دادهٔ بهتر است: یک مجموعهارزیابی واقعی از گفتگوهای مصاحبه، همراه با پیشنهادهایی که یک مشاور خوب واقعاً میداد، تا رتبهبندی بهجای بحثکردن اندازهگیری شود. بقیهٔ کارها — یک شاخهٔ دوم جریان، پنل غنیتر — نسبتاً سادهاند.
کل مستندات فنی بهصورت ویکی منتشر شده است: معماری، مرجع API، موتور تطبیق، غربال GHQ، پنل مدیریت، استقرار و عیبیابی. سورس روی GitHub با مجوز MIT.
مرتبط: نسخهٔ پیامرسانِ همین موتور — یک ربات بل با همان غربال GHQ و همان منطق تطبیق — در مقالهای جداگانه در همین سایت آمده است.