پشتیبان هوشمند مهمان؛ پشت صحنه ساختن یک مسیر تازه برای پشتیبانی تمام و عیار در جاباما

پشتیبان هوشمند مهمان؛ پشت صحنه ساختن یک مسیر تازه برای پشتیبانی تمام و عیار در جاباما

نقطه شروع قصه؛ سؤال‌های ساده، انتظارهای طولانی

در جاباما، روزهای اوج سفر، برای واحد پشتیبانی هم روزهای شلوغی هستند. همراه با بیشتر شدن رزروها، سؤال‌ها و درخواست‌ها از راه می‌رسند: شرایط کنسلی چیست؟ پرداخت انجام شده؟ چرا رزرو هنوز تأیید نشده؟ و گاهی هم مهمان به اقامتگاه رسیده و برای ورود به کمک فوری نیاز دارد.

پیش از شروع این پروژه، مهمان‌ها از طریق سامانه چت با کارشناسان پشتیبانی گفتگو می‌کردند. در زمان‌های شلوغ، کارشناسان ناچار بودند تماس‌ها را در اولویت بگذارند؛ چون معمولاً مسائل فوری‌تری پشت تلفن مطرح می‌شد. نتیجه این بود که پاسخ‌دادن به چت‌ها عقب می‌افتاد و زمان انتظار مهمان طولانی می‌شد.

دسترسی مستقیم به کال‌سنتر هم عمدتاً برای مهمان‌هایی فراهم بود که رزرو قطعی داشتند یا به‌تازگی اقامتشان تمام شده بود. بنابراین چت، برای بخشی از کاربران مسیر مهمی برای دریافت راهنمایی و پیگیری بود.

وقتی به جنس سؤال‌ها نگاه کردیم، دیدیم بسیاری از آن‌ها با خواندن یک راهنما یا بررسی اطلاعاتی ساده به جواب سؤالشان می‌رسندپاسخ می‌گیرند. گاهی جواب در پرسش‌های متداول بود؛ گاهی کافی بود وضعیت رزرو، پرداخت یا اطلاعات اقامتگاه را ببینیم. اطلاعات وجود داشت، اما رسیدن مهمان به پاسخ، همچنان به مدت زمانی که وقت یک کارشناس می‌توانست صرف کند وابسته بود.

اواسط خرداد ۱۴۰۵، ساخت پشتیبان دستیار هوشمند مهمان را از همین نقطه شروع کردیم. می‌خواستیم پاسخ و بررسی اولیه سریع‌تر آغاز شود و درخواست‌هایی که به حضور کارشناس نیاز دارند، با اطلاعات کافی به او برسند.

افزایش ظرفیت پاسخ‌گویی انسانی می‌توانست بخشی از فشار را کم کند، اما سؤال‌های تکراری همچنان وقت کارشناسان را می‌گرفتند. تغییر این مسیر دقیقاً یک فرصت طلایی برای شکستن ساختار قبل بود.

کشمکش‌ها و دوراهی‌ها؛ دستیار برای کمک‌کردن به چه چیزی نیاز دارد؟

اولین نسخه را با پرسش‌های متداول شروع کردیم. محدوده کار مشخص بود: محتوای مرتبط را پیدا کن و بر اساس آن پاسخ بده.

اما تفاوت دو سؤال، خیلی زود قدم بعدی را روشن کرد:

«پول رزرو لغوشده چطور برمی‌گردد؟»

«آیا پول رزرو من برگشته؟»

برای اولی، راهنما کافی است. برای دومی، باید اطلاعات همان مهمان و همان رزرو را بررسی کرد. دستیار برای مفیدترشدن، به ابزارهایی نیاز داشت که بتواند داده‌های واقعی را بخواند.

با اضافه‌شدن ابزارها، مسئله تازه‌ای پیش آمد: مدل باید درباره چه چیزی تصمیم‌گیری کند و چه چیزی باید به قواعد صریح سیستم متکی باشد؟

مرزی که انتخاب کردیم روشن بود: وظیفه مدل زبانی، فهم مسئله و انتخاب قدم بعدی است؛ دسترسی و اجرای عملیات را بک‌اند کنترل می‌کند.

مدل می‌تواند تشخیص بدهد که پاسخ به بررسی پرداخت نیاز دارد، اما بک‌اند تعیین می‌کند اطلاعات کدام رزرو در اختیارش قرار بگیرد. برای ثبت درخواست هم، نتیجه واقعی عملیات باید مبنای پاسخ باشد. گفتن «ثبت شد» از طرف مدل، به‌تنهایی به معنای ثبت‌شدن چیزی نیست.

این انتخاب، قراردادها، اعتبارسنجی و مدیریت خطای بیشتری می‌خواست. در عوض، می‌توانستیم توانایی دستیار را گسترش بدهیم و قواعد دسترسی و کسب‌وکار را در بخشی نگه داریم که رفتار آن قابل بررسی و تست است.

دوراهی دیگر، میان عمق بررسی و زمان پاسخ بود. هر ابزار تازه، امکان پاسخ‌گویی به سؤال‌های بیشتری را فراهم می‌کرد، اما هر رفت‌وبرگشت هم زمان می‌برد. باید یاد می‌گرفتیم چه اطلاعاتی را زودتر آماده کنیم، کدام بررسی واقعاً لازم است و در زمان انتظار، چه چیزی به مهمان نشان بدهیم.

اوج داستان؛ ساختن محصول، یک مسئله در هر قدم

معماری را با سه بخش اصلی پیش بردیم. فرانت‌اند با Next.js و کیت کامپوننت‌هایی ساخته شد که جاباما از قبل توسعه داده بود. بک‌اند Go، مسئول داده‌های ماندگار، دسترسی‌ها، قواعد کسب‌وکار و ارتباط با سرویس‌های جاباماست. هسته ایجنت هم با Python و LangGraph، چرخه گفتگو و استفاده از ابزارها را مدیریت می‌کند.

در بک‌اند، تاریخچه گفتگوها و اطلاعات ماندگار را در PostgreSQL نگه می‌داریم. Redis برای کش‌کردن بعضی داده‌های پرتکرار و پشتیبانی از صف کارهای پس‌زمینه استفاده می‌شود. کارهایی که باید مستقل از درخواست کاربر ادامه پیدا کنند، از طریق این صف پردازش می‌شوند. خود گفتگو مسیر دیگری دارد: بک‌اند درخواست را به هسته ایجنت می‌فرستد، ابزارهای موردنیاز در جریان بررسی فراخوانی می‌شوند و پاسخ به‌صورت تدریجی به مهمان برمی‌گردد.

این تقسیم کار کمک می‌کرد مسئولیت هر بخش روشن بماند. هسته ایجنت روی گفتگو و انتخاب قدم بعدی تمرکز می‌کرد و نگهداری داده، کنترل دسترسی و اجرای عملیات در اختیار بک‌اند می‌ماند.

برای طراحی هارنس ایجنت، سراغ پروژه‌های متن‌باز مختلف، از جمله OpenCode و PI رفتیم. می‌خواستیم ببینیم مدل، ابزارها، زمینه گفتگو و وضعیت اجرا را چطور کنار هم مدیریت می‌کنند. از این الگوها برای ساخت محیطی الهام گرفتیم که با نیازهای پشتیبانی جاباما سازگار باشد.

از جواب‌های عمومی تا اطلاعات همان مهمان

مسیر توسعه با بازیابی پرسش‌های متداول شروع شد. گفتگوهای سامانه چت قبلی را با کمک ایجنت‌ها تحلیل کرده بودیم و حدود ۸۲٪ پرسش‌ها ماهیت اطلاعاتی داشتند. جواب بخشی از آن‌ها در راهنماها بود و بخشی هم به بررسی اطلاعات رزرو یا پرداخت نیاز داشت. از دسته اول شروع کردیم: FAQها را embed کردیم تا دستیار بتواند بر اساس معنای سؤال، محتوای مرتبط را پیدا کند؛ حتی وقتی مهمان سؤالش را با کلمات متفاوتی می‌پرسد. امکان افزودن و embed کردن محتوای تازه را هم فراهم کردیم تا این دانش همراه با محصول به‌روز شود.

برای سؤال‌هایی که به وضعیت خود مهمان مربوط می‌شدند، قدم دوم را برداشتیم: ابزارهایی برای بررسی رزرو، پرداخت، حساب کاربری و اطلاعات اقامتگاه ساختیم و از طریق فراخوانی ابزار یا Tool Calling در اختیار ایجنت گذاشتیم. مدل بر اساس سؤال، ابزار و ورودی‌های موردنیاز را انتخاب می‌کند؛ بک‌اند پس از کنترل دسترسی، اطلاعات را از سرویس مربوط می‌گیرد و نتیجه به مدل برمی‌گردد. دستیار حالا می‌توانست راهنمای عمومی را کنار وضعیت واقعی مهمان بگذارد و پاسخ مشخص‌تری بدهد.

با اضافه‌شدن ابزارها، مسئله دیگری خودش را نشان داد: تعداد پیام‌ها در هر نشست زیاد می‌شد و رسیدن به پاسخ، رفت‌وبرگشت بیشتری می‌خواست. بخشی از این مسیر صرف جمع‌آوری اطلاعات پایه می‌شد؛ ایجنت برای شناخت وضعیت مهمان سؤال می‌پرسید یا دوباره سراغ ابزارهایی می‌رفت که اطلاعات مشابهی را می‌خواندند. با خودمان گفتیم چطور می‌توانیم این اطلاعات را از ابتدا در اختیارش بگذاریم تا هر بار مجبور نباشد از نو آن‌ها را جمع کند؟ راه‌حل، آماده‌کردن زمینه اولیه گفتگو بود: بخشی از اطلاعات پایه و مجاز مهمان را در شروع نشست دریافت کردیم و در اختیار ایجنت گذاشتیم تا در ادامه به همان زمینه رجوع کند. به این ترتیب، سؤال‌ها و فراخوانی‌های تکراری کمتر می‌شد و گفتگو زودتر به اصل مسئله می‌رسید. البته اطلاعات متغیری مثل وضعیت پرداخت، همچنان در زمان نیاز دوباره بررسی می‌شوند.

قدم بعد، ساده‌ترکردن اقدام بعدی مهمان بود. امکان نمایش دکمه در همان نشست گفتگو را اضافه کردیم تا دستیار بتواند در جای مناسب، گزینه‌های مشخصی برای ادامه مسیر پیش روی مهمان بگذارد. به این ترتیب، بخشی از رفت‌وبرگشت‌ها با انتخاب یک دکمه جلو می‌رفت و نیازی به تایپ پاسخ تازه نبود. در کنار این دکمه‌ها، دیپ‌لینک‌های بخش‌های مرتبط اپ جاباما را هم اضافه کردیم تا مهمان از دل گفتگو مستقیم به صفحه موردنیاز برسد. می‌خواستیم هرجا پاسخ به انجام کاری منتهی می‌شود، راه انجام آن هم همان‌جا در دسترس باشد.

وقتی یک سؤال به چند بررسی نیاز داشت

در خود گفتگو هم کم‌کم امکان اجرای چند فراخوانی ابزار در یک نوبت یا turn را فراهم کردیم. یک پیام مهمان می‌توانست به چند مرحله بررسی نیاز داشته باشد؛ مثلاً پیدا‌کردن رزرو، خواندن جزئیات آن و بعد بررسی پرداخت. ایجنت می‌توانست نتیجه هر ابزار را برای انتخاب قدم بعدی استفاده کند و این مسیر را تا آماده‌شدن پاسخ پیش ببرد، بدون اینکه مهمان برای جلو رفتن هر مرحله پیام تازه‌ای بفرستد.

این قابلیت، نیاز به کنترل بیشتری هم داشت. اگر ایجنت مدام همان ابزار را صدا می‌زد، تعداد فراخوانی‌ها بالا می‌رفت اما بررسی لزوماً جلو نمی‌رفت. برای بعضی متدها در لایه ابزارها گارد گذاشتیم تا فراخوانی تکراری و بی‌نتیجه دوباره اجرا نشود؛ برای ادامه چرخه استفاده از ابزارها هم حد مشخصی در نظر گرفتیم تا گفتگو در یک حلقه بی‌پایان گیر نکند. حالا علاوه بر اینکه ایجنت چه ابزاری را انتخاب می‌کند، باید حواسمان به این هم می‌بود که چه زمانی ادامه‌دادن دیگر کمکی به پاسخ نمی‌کند.

کنترل تکرار ابزارها، یک بخش ماجرا بود؛ بخش دیگر این بود که ایجنت بعد از خطا چطور کار را ادامه بدهد. برای نمونه، آماده‌کردن تیکت ممکن بود به‌خاطر ناقص‌بودن اطلاعات متوقف شود. تکرار همان درخواست چیزی را حل نمی‌کرد و یک پیام کلی مثل «عملیات ناموفق بود» هم به ایجنت نمی‌گفت قدم بعدی چیست. برای این مسیر، بک‌اند خطا را به‌صورت ساختاریافته برمی‌گرداند: چه اطلاعاتی کم است یا کدام ورودی نیاز به اصلاح دارد. لایه ابزار این نتیجه را به راهنمای مشخصی برای مدل تبدیل می‌کند تا اطلاعات لازم را از مهمان بگیرد و بعد درخواست اصلاح‌شده را بفرستد.

برای همه خطاها هم یک مسیر یکسان در نظر نگرفتیم. اگر اطلاعاتی از پاسخ مهمان لازم بود، کنترل به مدل برمی‌گشت تا گفتگو را ادامه بدهد و پاسخ را وارد درخواست بعدی کند. اما اگر مدرکی مثل تصویر کم بود، خود سیستم رابط بارگذاری را نمایش می‌داد و اجرای آن نوبت را متوقف می‌کرد تا فایل برسد. در یکی از نسخه‌ها، جمع‌آوری اطلاعات متنی را هم با توقف خودکار و یک سؤال ثابت انجام داده بودیم؛ نتیجه این شد که پاسخ مهمان وارد درخواست بعدی نمی‌شد و همان سؤال دوباره تکرار می‌شد. با برگرداندن این بخش به چرخه گفتگو، مسیر اصلاح کامل شد: تشخیص خطا، دریافت اطلاعات لازم و ادامه کار از همان نقطه.

این تجربه در ادامه بارها به کارمان آمد: بهترشدن دستیار، همیشه به تغییر مدل نیاز نداشت. گاهی باید ابزار را روشن‌تر تعریف می‌کردیم یا اطلاعات بهتری برای تصمیم‌گیری در اختیار مدل می‌گذاشتیم.

دیدن گفتگو از سمت مهمان

در ادامه، Judge را ساختیم تا با کمک مدل زبانی، گفتگوهای پایان‌یافته را از نظر مرتبط‌بودن پاسخ، لحن و روانی فارسی ارزیابی کنیم. این بررسی جدا از پاسخ‌گویی زنده انجام می‌شد. نظرسنجی و بازخورد مهمان‌ها را هم کنار آن گذاشتیم؛ می‌خواستیم هم کیفیت پاسخ را بررسی کنیم و هم ببینیم تجربه گفتگو برای خود مهمان چطور بوده است. بررسی پاسخ‌ها و نظر کاربران کمک می‌کرد بفهمیم کجا گفتگو طولانی شده، کجا پاسخ روشن نیست و کجا پیگیری به بن‌بست می‌رسد؛ و اصلاح‌های بعدی را از همان‌جا شروع کنیم.

هم‌زمان، زمان انتظار بیشتر به چشم آمد. دستیار ممکن بود مشغول چند بررسی ضروری باشد، اما مهمان فقط منتظر آماده‌شدن پاسخ بود. نمایش پیشرفت را اضافه کردیم تا مشخص باشد دستیار در حال بررسی رزرو یا خواندن تراکنش‌هاست.

تبدیل گفتار به متن هم از یک نیاز ساده آمد: شرح‌دادن مشکل با تایپ، روی موبایل و وسط سفر، همیشه راحت نیست. این قابلیت را اضافه کردیم تا مهمان بتواند مسئله‌اش را بگوید و گفتگو با متن حاصل از آن ادامه پیدا کند. در عمل، استفاده از آن محدود بود؛ اما در بررسی گفتگوهایی که با این قابلیت پیش رفته بودند، دیدیم مهمان‌ها جزئیات بیشتری از مسئله‌شان بیان می‌کردند. همین مشاهده، کاربرد مشخص آن را برایمان روشن‌تر کرد: کمک به مهمان‌هایی که برای توضیح کامل‌تر مشکل، با صحبت‌کردن راحت‌ترند.

این محصول با همین چرخه جلو رفت: استفاده واقعی، مشاهده مانع، اصلاح و انتشار دوباره. برای اینکه بتوانیم این چرخه را مرتب تکرار کنیم، روی شیوه توسعه خود محصول هم کار کردیم.

یک بیلدر، چند ایجنت و یک چرخه مشترک توسعه

توسعه سرتاسری این اپ را یک نفر از کامیونیتی بیلدرهای جاباما، با کمک روزانه چند ایجنت توسعه‌دهنده پیش برد. اینجا منظور، ایجنت‌هایی است که روی ساخت محصول کار می‌کردند؛ از پیاده‌سازی تغییرها تا نوشتن و اجرای تست و بررسی نتیجه. مسئولیت تعریف مسئله، تصمیم‌های محصول و معماری و جمع‌بندی تغییرها با بیلدر بود. برای استفاده مؤثر از ایجنت‌ها، باید هر مسئله به کاری مشخص و قابل بررسی تبدیل می‌شد: چه رفتاری باید تغییر کند، محدوده تغییر کجاست و از کجا می‌فهمیم درست انجام شده است؟

واحد کارمان ایشو بود. به‌جای سپردن یک درخواست بزرگ و مبهم به ایجنت، تغییر را با هدف، محدوده و معیار پذیرش مشخص تعریف می‌کردیم. کار را طوری چیدیم که سه ایشو بتوانند هم‌زمان پیش بروند. هر مسیر، فضای کاری مستقل خودش را داشت تا تغییرهای در حال انجام با هم مخلوط نشوند و نتیجه هرکدام جداگانه قابل بررسی باشد.

این استقلال فقط در کد نبود. روی یک دستگاه، چند نمونه مستقل از اپ اجرا می‌شد و ایجنت‌ها می‌توانستند تغییراتشان را در محیط مربوط به خود ببینند. فاصله میان «کد تغییر کرد» و «رفتار محصول را دیدیم» کوتاه‌تر می‌شد.

اما چند مسیر موازی، به روشی مشترک برای بررسی نتیجه نیاز داشتند. برای قابلیت‌های اپ تست نوشتیم و هارنس تست را طوری آماده کردیم که ایجنت بتواند فهرست آزمون‌ها را ببیند، تست مرتبط با تغییرش را انتخاب کند و خروجی قابل استفاده بگیرد. هنگام خطا، گزارش باید نشان می‌داد کدام بررسی شکست خورده و شواهد لازم برای پیگیری کجاست. به این ترتیب، ایجنت می‌توانست وارد چرخه کوتاهِ تغییر، اجرا و اصلاح شود.

تست‌ها هم متناسب با تغییر انتخاب می‌شدند. برای یک اصلاح محدود، آزمون مرتبط را اجرا می‌کردیم؛ وقتی رفتار از مرز چند سرویس عبور می‌کرد، سراغ تست یکپارچه می‌رفتیم. برای مثال، در تست مسیر چت، پیام از فرانت‌اند وارد بک‌اند و هسته ایجنت می‌شود، پاسخ به‌صورت تدریجی برمی‌گردد و ذخیره‌شدن آن در تاریخچه بررسی می‌شود. این مسیر در محیطی مستقل و با سرویس مدل شبیه‌سازی‌شده اجرا می‌شود تا بررسی جریان نرم‌افزار به پاسخ متغیر یک مدل بیرونی وابسته نباشد. خود هارنس هم تست دارد؛ از مدیریت زمان اجرا تا جمع‌کردن محیط آزمون و گزارش خطا.

خروجی هر مسیر توسعه، تغییر کد همراه با نتیجه بررسی‌هایش بود. این خروجی‌ها مبنای بازبینی و یکپارچه‌کردن کار قرار می‌گرفتند و بعد، نسخه تازه وارد چرخه انتشار و مشاهده رفتار واقعی می‌شد. برای ما، بخش مهم توسعه با ایجنت‌ها همین بود: مسئله را روشن تعریف کنیم و محیطی بسازیم که نتیجه کار در آن قابل مشاهده، آزمودن و اصلاح‌کردن باشد.

تا زمان آماده‌کردن این روایت، کمی بیش از صد روز از شروع پروژه گذشته و در این مدت، بیش از ۴۰۰ نسخه از محصول منتشر شده است. این تعداد انتشار، حاصل همان اصلاح‌های پیوسته‌ای است که از استفاده واقعی محصول بیرون آمده‌اند.

دستاوردها و سنجه‌ها؛ مقیاسی که حالا می‌توان دید

در بازه نمایش‌داده‌شده در نمودار، پشتیبان هوشمند ۵۴٬۰۳۱ نشست گفتگو با ۳۵٬۹۶۰ مهمان یکتای شناسایی‌شده داشته است. برای مقایسه اندازه استفاده، سامانه چت قبلی در حدود سه سال نزدیک به صد هزار نشست ثبت کرده بود. این مقایسه، حجم استفاده را نشان می‌دهد؛ میزان حل مسئله را باید با سنجه‌های جداگانه بررسی کرد.

در ۳۰ روز کامل منتهی به تاریخ پایان نمودار، میانگین استفاده حدود ۶۷۱ نشست در روز بوده است. بیشترین حجم ثبت‌شده در بازه نمودار نیز ۱٬۰۱۵ نشست در یک روز است.

برای تصور مقیاس انسانی، اگر هر نشست فقط پنج دقیقه وقت بخواهد، هزار نشست معادل بیش از ۸۳ ساعت کار، یا بیش از ده شیفت هشت‌ساعته است. این محاسبه صرفاً برای ملموس‌کردن حجم گفتگوست و میزان صرفه‌جویی اندازه‌گیری‌شده ما نیست.

برای مهمان، نتیجه ملموس این است که پاسخ و بررسی اولیه زودتر آغاز می‌شود. سؤال‌هایی که با راهنما یا خواندن اطلاعات پاسخ می‌گیرند، در همان گفتگو پیش می‌روند. موارد نیازمند پیگیری انسانی هم با اطلاعات جمع‌آوری‌شده به تیکت تبدیل می‌شوند تا کارشناسان بتوانند کار را با شناخت بیشتری از مسئله شروع کنند.

نگاه به عقب؛ کیفیت دستیار در جزئیات ساخته شد

وقتی به این مسیر نگاه می‌کنیم، بخش زیادی از پیشرفت محصول در جزئیاتی اتفاق افتاده که در نگاه اول کوچک به نظر می‌رسند: زمینه‌ای که پیش از شروع گفتگو آماده می‌شود، گزینه‌هایی که ابزار به مدل نشان می‌دهد، خطایی که راه اصلاح دارد یا وضعیتی که انتظار را برای مهمان قابل فهم می‌کند.

مدل برای کمک‌کردن، به محیط مناسبی برای کار نیاز دارد. ابزار باید روشن باشد، داده باید معتبر باشد و نتیجه عملیات باید قابل تشخیص باشد. هر ابهام در این بخش‌ها، می‌تواند به سؤال اضافه، پاسخ مبهم یا پیگیری ناتمام برای مهمان تبدیل شود.

تجربه توسعه با ایجنت‌ها هم همین درس را در مقیاسی دیگر به ما داد. هرچه امکان تولید و تغییر کد بیشتر می‌شود، تعریف دقیق مسئله و امکان بررسی نتیجه اهمیت بیشتری پیدا می‌کند. تست، محیط مستقل و قرارداد روشن، ابزارهای روزمره این شیوه توسعه‌اند.

اگر دوباره از ابتدا شروع می‌کردیم، قرارداد ابزارها، ارزیابی گفتگو و هارنس تست را زودتر کنار نسخه اولیه قرار می‌دادیم. این‌ها کمک می‌کنند زودتر بفهمیم مشکل از کجاست و با اطمینان بیشتری قدم بعدی را برداریم.

دستیار هوشمند مهمان همچنان در حال توسعه است. قدم بعدی، استفاده از این تجربه برای دستیار میزبان خواهد بود؛ با سؤال‌ها و نیازهای متفاوت آن سوی بازار.

ریلیز تابستان ۱۴۰۵، فرصتی برای روایت این بخش از مسیر است. نسخه‌های بعدی هم از همان جایی شروع می‌شوند که نسخه اول شروع شد: مهمانی سؤالی دارد و انتظار دارد کارش پیش برود.