تصور کنید رابط کاربری محصولتان در ۶ ساعت آماده شده اما شما دو هفته کامل را صرف جنگ با باگهایی میکنید که حتی در گزارشهای خطا هم ظاهر نمیشوند. این کابوسِ توسعهدهندهای است که قصد داشت یک افزونه برای ارسال محتوا به مدلهای هوش مصنوعی بسازد.
این محصول از رابط برنامهنویسی پنل کناری کروم (Chrome Side Panel API)، DOM ساده و لاگهای کنسول در اسکریپتهای محتوا (content scripts) استفاده میکند تا متنهای هایلایتشده یا اسکرینشاتها را به مقصدهایی مثل ChatGPT، Claude، Gemini یا اهداف سفارشی بفرستد. عملکرد اصلی بسیار ساده است: نوار کناری به کاربر اجازه میدهد یک تب AI مقصد را انتخاب کرده و محتوا را همراه با یک پرامپت کوتاه (wrapper prompt) ارسال کند.
طبق گفتهی این توسعهدهنده، مشکل اصلی جایی شروع شد که او رابط کاربری (UI) را اولویت داد و مدل وضعیت — یعنی همان حافظهی کوتاهمدتی که میگوید در هر لحظه چه اتفاقی در برنامه میافتد — را نادیده گرفت. ساخت افزونههای مرورگر اغلب از یک «شکاف دید» رنج میبرد؛ جایی که توسعهدهندگان رابط کاربری را بر مدل وضعیت ترجیح میدهند. بسیاری از پستهای «من X را در Y ساعت ساختم» عملاً نیمی از مسیر را پنهان میکنند؛ یعنی همان بخشی که زمان واقعی در آن صرف میشود.
این موضوع بهویژه برای ابزارهایی که با تبهای خارجی تعامل دارند صادق است، جایی که وضعیت «فعال بودن» (active) اغلب یک توهم است. برای کسانی که به برنامههای وب استاندارد عادت کردهاند، محیط چند-تبه (Multi-tab) مرورگر لایهای از ناپایداری ایجاد میکند که میتواند بهصورت خاموش تجربه کاربر را تخریب کند.
به نقل از مستندات این پروژه، چهار شکست فنی مشخص رخ داد. این باگها همیشه باعث کرش کردن برنامه یا ثبت خطا در کنسول نمیشدند؛ بلکه تضادی میان مدل ذهنی کاربر (آنچه کاربر فکر میکرد اتفاق میافتاد) و وضعیت فنی افزونه ایجاد میکردند:
تلهی شناسایی تبها
- باگ: کاربر دو تب ChatGPT (یکی برای فضای کاری/Workspace و یکی شخصی) باز داشت، اما پیامها به تبی میرفتند که آخرینبار روی آن کلیک شده بود، نه لزوماً تبی که کاربر قصد ارسال به آن را داشت.
- مکانیزم: توسعهدهنده در ابتدا از دستور
chrome.tabs.query({active: true})استفاده کرده بود که تب فعال فعلی را برمیگرداند. با این حال، تب فعال لزوماً همان مقصدی نیست که کاربر در ذهن دارد. - راهکار: منطق برنامه تغییر کرد تا هر مقصد AI در لحظه بوت شدن افزونه (boot)، یک شناسهی تب (Tab ID) ثابت را ثبت کند. اکنون منطق ارسال بهجای تکیه بر فوکوس پنجره، در این فهرست ثبتشدهها جستجو میکند. پیادهسازی این بازطراحی یک صبح زمان برد و انتقال جریانهای موجود به این سیستم جدید، یک بعدازظهر طول کشید.
ناهماهنگی در اسکرینشاتها
- باگ: کاربر بخشی از یک بلوک کد را اسکرینشات میگرفت، روی گزینه «توضیح» (annotate) کلیک میکرد و یک کادر قرمز دور خطوط ۱۲ تا ۱۵ میکشید. هرچند پرداختن به حاشیهها درست به نظر میرسید، اما هوش مصنوعی بایتهای تصویر اولیه را دریافت میکرد که در لحظه اول ظاهر شدن نوار ابزار کپچر شده بود.
- تأثیر: این یک تخریب خاموش بود و شناسایی آن نیاز به سه گزارش مجزا از کاربران داشت، چون در بازبینی بصری، اسکرینشاتها «درست» به نظر میرسیدند.
- راهکار: پنل کناری دیگر به اسکرینشات موجود در حافظه اعتماد نمیکند. راهکار شامل ایجاد یک شیء واحد به عنوان «منبع حقیقت» (single source-of-truth) برای ناحیه تصویر بود که هم بوم نقاشی (canvas) و هم لایهی شبکه دادهها را از آن میخوانند، یا اینکه در هر رویدادِ حاشیهنویسی، تصویر مجدداً کپچر شود.
دروغِ کتابخانه پرامپتها
- باگ: قابلیت «کتابخانه پرامپت» برای استفادههای بعدی، ارسالهای موفق را ثبت میکرد. این سیستم متن پرامپتی را که کاربر تایپ کرده بود ذخیره میکرد، اما محتوای منبعِ بستهبندی شده (wrapped source content) یا اسکرینشاتهایی که بهطور خودکار تزریق و ارسال شده بودند را ذخیره نمیکرد.
- نتیجه: وقتی کاربر یک مورد از کتابخانه را سه روز بعد دوباره اجرا میکرد، پاسخ متفاوتی میگرفت چون محتوای صفحه وب اصلی تغییر کرده بود و پرامپت قدیمی با دادههای جدید ترکیب میشد.
- راهکار: توسعهدهنده تصمیم گرفت ذخیرهی متن پیش از پردازش (pre-wrap) را یک «دروغ» بداند. اکنون کتابخانه، محتوای استخراجشده در لحظهی ارسال را ذخیره میکند. این تغییر باعث شد ویژگی مذکور بیشتر شبیه به تاریخچه مرورگر (browser history) باشد تا تاریخچه چت.
شبح وضعیتهای ماندگار
- باگ: پنل کناری کروم حتی پس از بسته شدن به صورت پاپآپ، زنده میماند. اگر کاربر پنل را میبست و فردای آن روز بازمیگشت، وضعیت (state) همچنان باقی بود، اما تبِ مقصد AI در اکثر موارد بسته شده بود.
- راهکار: یک بررسی «اعتبار وضعیت» (still valid check) برای هر شیء وضعیت در لحظه باز شدن پنل تعریف شد، نه فقط در لحظه کلیک. اگر شناسهی تب ثبتشده دیگر وجود نداشته باشد، افزونه آن را از فهرست حذف کرده و یک جایگاه (placeholder) با پیام «این مقصد دیگر متصل نیست» نمایش میدهد.
این تجربه ثابت میکند وضعیت در افزونههای مرورگر در نقاطی پراکندهتر از آن چیزی است که توسعهدهندگان در ابتدا تصور میکنند. شکاف بین جایی که کاربر انتظار دارد دادهها باشند و جایی که کد واقعاً آنها را ذخیره میکند، همان جایی است که بحرانیترین باگها پنهان شدهاند. توسعهدهنده اشاره کرد که وسوسه این است که بررسی وجود تب را فقط یکبار بنویسید، اما درس واقعی این است که باید این بررسی را برای هر فیلد وضعیت جدیدی که اضافه میکنید، دوباره بنویسید.
برای حل این مشکل، یک قانون سختگیرانه وضع شد: هر فیلد وضعیت باید دقیقاً یک نویسنده (Writer) و یک خواننده (Reader) داشته باشد. اگر دو مؤلفه نیاز به خواندن یک فیلد داشتند، یکی باید از طریق دیگری بخواند. اگرچه این کار جلوی تمام تخلفات را نگرفت، اما زمان بین ایجاد یک باگ و شناسایی قانون شکسته شده را کوتاه کرد.
برای کسانی که ابزارهای مرورگر یکپارچه با AI میسازند، رابط کاربری بهندرت گلوگاه است. چالش مهندسی واقعی، مدیریت رابطه شکننده بین حافظه افزونه و وضعیت ناپایدار تبهای مرورگر است.
گام بعدی شما
- اگر در حال توسعه ابزارهای مرورگر هستید، بهجای تکیه بر
active: trueبرای شناسایی تبها، سیستم ثبت ID ثابت را پیاده کنید. - برای هر فیلد داده در حافظه، یک لایهی اعتبارسنجی (Validation) در لحظه ورود کاربر (On-mount) قرار دهید.
- منطق «یک نویسنده-یک خواننده» را برای مدیریت وضعیتهای پیچیده در حافظه موقت به کار ببرید.
اما مدیریت این وضعیتهای شکننده تنها نیمی از مسیر است؛ چالش بعدی، بهینهسازی هزینه استنتاج در مقیاسهای بزرگ است که در گزارشهای آتی بررسی خواهیم کرد. در همین راستا، ابزارهایی مانند TokenMizer تلاش میکنند تا هزینههای توکنهای برنامهنویسی را از طریق گرافسازی جلسات کاهش دهند تا مدیریت کانتکست در مقیاس وسیع بهینه شود.




گفتگو