این مجموعه توسط Claude (دستیار هوشمصنوعی شرکت Anthropic) نوشته شده — بر اساس یک همکاری فنی
طولانی و واقعی با حمیدرضا محمدی، نگهدارندهٔ گنجور، در ساخت این قابلیت. جزئیات و مثالهایی که در
این بخش میبینید، همه از همان روند واقعی توسعه گرفته شدهاند.
یادآوری کوتاه، و سؤال این بخش
در بخشهای ۱ و ۲، زیرساخت پایه کامل شد: بردارها ساخته شدند، و سرویس زنده هم — با جداسازی کامل از
بقیهٔ سایت — میتوانست جستجوی کاربر را به بردار تبدیل کند و نزدیکترین شعرها را برگرداند. اما
همین «نزدیکترین شعرها را پیدا کن» بهتنهایی، در عمل، برای استفادهٔ واقعی کافی نبود. سه مشکل
واقعی که بعد از راهاندازی اولیه پیدا شدند، موضوع این بخشاند.
مشکل اول: جستجوی «شاهنامه» صفر نتیجه میداد
اگر کاربری تایپ میکرد «داستانهای شاهنامه دربارهٔ رستم»، انتظار منطقی این بود که فقط بین
شعرهای فردوسی جستجو شود، نه بین کل گنجور. برای این کار، یک مکانیزم ساده اضافه شد: اگر جملهٔ
کاربر شامل نام یک شاعر یا یک کتاب/مجموعه باشد (تطبیق متنی ساده، نه هوش مصنوعی — همانطور که در
بخش ۱ هم اشاره شد)، جستجو فقط به شعرهای همان شاعر/کتاب محدود میشود.
اما نسخهٔ اول این محدودسازی یک باگ ظریف داشت: وقتی محدوده «شاهنامه» تشخیص داده میشد، جستجو فقط
شعرهایی را در نظر میگرفت که مستقیماً زیر همان دستهٔ «شاهنامه» بودند — نه شعرهایی که چند لایه
پایینتر، زیر زیردستههای آن (مثل «داستان رستم و سهراب»، «پادشاهی کیقباد» و دهها زیردستهٔ دیگر)
قرار داشتند. چون گنجور دستهبندیها را بهصورت تودرتو (نه تخت) نگه میدارد — یک کتاب، خودش
چند بخش دارد، هر بخش چند فصل، و شعرها معمولاً زیر پایینترین لایه قرار میگیرند — نتیجه این بود
که این محدودسازی عملاً هیچ شعری پیدا نمیکرد، چون هیچ شعری مستقیماً زیر خودِ دستهٔ ریشهٔ
«شاهنامه» نبود.
راهحل: بهجای نگاهکردن فقط به یک لایه، کل زیردرخت دستهبندی را پیمایش کردیم — یعنی از دستهٔ
تشخیصدادهشده شروع میکنیم و همهٔ زیردستهها را، هرچقدر هم عمیق باشند، جمع میکنیم؛ بعد جستجو
را به مجموعهٔ کامل شعرهای زیر همهٔ آنها محدود میکنیم.
یک ظرافت دیگر هم لازم بود: بعضی از این نامهای دسته، مبهماند. مثلاً «غزلیات» هم زیر دیوان حافظ
هست، هم زیر دیوان صدها شاعر دیگر. اگر کاربر فقط بنویسد «غزلیات عاشقانه»، منطقی نیست جستجو را به
یک شاعر خاص محدود کنیم — چون معلوم نیست منظورش کدام شاعر است. برای همین، اینجور نامهای مبهم
فقط زمانی بهعنوان محدودکننده در نظر گرفته میشوند که اسم یک شاعر مشخص هم در همان جمله
آمده باشد (مثلاً «غزلیات حافظ»).
و چون هیچ تشخیص خودکاری همیشه درست نیست، یک راه فرار هم اضافه شد: یک لینک «(جستجوی سراسری)» که
با یک کلیک، محدودیت تشخیصدادهشده را کنار میگذارد و در کل گنجور جستجو میکند — برای وقتی که
تشخیص خودکار اشتباه حدس زده.
مشکل دوم: از داخل یک شعرِ پیداشده، کدام بیت را نشان بدهیم؟
فرض کنید جستجو یک غزلِ ۹ بیتی حافظ را بهعنوان نتیجه پیدا کرده. نشاندادن فقط عنوان شعر کافی
نیست — کاربر میخواهد ببیند کدام بخش از این شعر به جستجویش مرتبط بوده. اینجا دقیقاً همان
مکانیزم سومی است که در بخش ۱ به آن اشاره کردیم (وقتی دربارهٔ جملهٔ «شعری در مورد بیوفایی دنیا
پیدا کن» صحبت کردیم): یک فهرست از کلمات کممعنا و پرتکرار (مثل «شعری»، «در مورد»، «پیدا کن») از
جملهٔ کاربر حذف میشود، و کلمات باقیمانده با متن هر بیت (و با CoupletSummary آن، همان خلاصهٔ
در سطح بیت که در بخش ۴ بیشتر دربارهٔ آن صحبت خواهیم کرد) مقایسه میشوند — بیتی که بیشترین
همپوشانی کلمهای را داشته باشد، بهعنوان پیشنمایش انتخاب میشود. اگر هیچ بیتی همپوشانی
مشخصی نداشت، بهسادگی بیت آغازین شعر نشان داده میشود.
نکتهٔ مهم دوباره: این مرحله کاملاً بعد از پیداشدن شعر توسط جستجوی معنایی اتفاق میافتد — در
تصمیم اینکه اصلاً کدام شعرها نتیجه باشند، هیچ نقشی ندارد؛ فقط تصمیم میگیرد از داخل یک نتیجهٔ
از قبل پیداشده، کدام تکه را نشان دهد.
مشکل سوم: خلاصههای هوشمصنوعی، خلاصههای ویرایششده توسط انسان
همانطور که در بخش ۱ اشاره شد، بخشی از خلاصههای شعرها (PoemSummary) هنوز بهطور خام
تولیدشده با هوش مصنوعیاند (با پیشوند «هوش مصنوعی:» مشخص میشوند)، و بخشی دیگر توسط کاربران
انسانی بازبینی و ویرایش شدهاند. در عمل، خلاصههای ویرایششده معمولاً دقیقتر و باکیفیتترند.
برای اینکه این تفاوت کیفیت در نتیجههای جستجو هم منعکس شود، بدون اینکه خلاصههای هوشمصنوعی
بهطور کامل کنار گذاشته شوند، یک تعدیل کوچک اضافه شد: امتیاز شباهت شعرهایی که هنوز خلاصهٔ
هوشمصنوعی خام دارند، در ۰.۹۷ ضرب میشود — یعنی فقط ۳٪ کاهش، نه حذف. این عدد هم در تنظیمات
سرویس (AiSummaryScorePenalty) قابل تغییر است، نه در کد قفلشده.
یک جزئیات ظریف اما مهم دربارهٔ صداقت در نمایش نتیجه: عددی که به کاربر نشان داده میشود (مثلاً
«۸۷٪ شباهت»)، همان امتیاز واقعی و تعدیلنشدهٔ شباهت کسینوسی است — نه نسخهٔ ۰.۹۷-ضربشده.
یعنی این تعدیل فقط روی ترتیب نتیجهها تأثیر میگذارد (کدام شعر بالاتر یا پایینتر قرار بگیرد)،
نه روی عددی که کاربر واقعاً میبیند. برای اینکه این تعدیل بیاثر نشود، مجموعهٔ اولیهٔ نامزدها هم
سهبرابر بزرگتر از تعداد نهایی نتیجهها در نظر گرفته میشود — تا بعد از تعدیل و مرتبسازی
دوباره، جای کافی برای واقعاً بالاترآمدن نتیجههای باکیفیتتر وجود داشته باشد.
یک نکتهٔ کوچک پایانی: چطور بفهمیم این قابلیت واقعاً مفید است؟
برای هر جستجو، یک رکورد ساده ثبت میشود: متن جستجو، تعداد نتیجهها، بالاترین امتیاز شباهت، و
اینکه آیا محدودیت شاعر/کتاب تشخیص داده شده یا نه. اگر کاربر روی یکی از نتیجهها کلیک کند، آن هم
(با رتبهٔ نتیجه در آن لحظه) ثبت میشود.
نکتهٔ مهم دربارهٔ حریم خصوصی: این ثبتها هیچ شناسهٔ کاربر یا آدرس IPای ذخیره نمیکنند — فقط
همان چند عدد و متن بالا. هدف این دادهها هم مشخص است: فهمیدن اینکه کجاهای واقعی جستجو ضعیف عمل
میکند (مثلاً جستجوهایی که کاربر روی هیچ نتیجهای کلیک نمیکند) — نه دنبالکردن رفتار افراد.
در بخش بعدی و پایانی این مجموعه، به سراغ ایدهای میرویم که این سه ویژگی هم زمینهساز آن بودند:
اگر بهجای خلاصهٔ کل یک شعر، برای هر بیت هم جداگانه بردار بسازیم چه اتفاقی میافتد؟ یک
آزمایش کوچک روی غزلهای حافظ، و بعد آمادهسازی کامل این ایده برای کل مجموعهٔ گنجور.