اگر امروز یک رابط کاربری چت ساده میسازید، احتمالاً متوجه شدهاید که فاصله بین یک دموی جذاب و یک محصول پایدار، در جزئیاتی نهفته است که فقط هنگام تایپ واقعی کاربران آشکار میشوند. چرا ساخت یک دموی ساده چت هوش مصنوعی تنها یک بعدازظهر زمان میبرد، اما یک رابط کاربری آماده برای تولید (Production-ready) نیازمند حل دهها مورد خاص (Edge Case) است که تنها زمانی ظاهر میشوند که کاربران واقعی شروع به تایپ کنند؟ برای توسعهدهندهای که میخواهد ابزاری بسازد که در مقیاس تولید شکست نخورد، مدیریت وضعیتهای پیچیده مدل بسیار حیاتیتر از نمایش ساده متن است.
بسیاری از برنامهنویسان از اتصال ساده رشتهها (String Concatenation) برای نمایش پاسخها استفاده میکنند، اما رابطهای کاربری حرفهای باید با پیامها به عنوان «بخشهای ساختاریافته» برخورد کنند تا استدلالها، فراخوانی ابزارها و ارجاعات بدون بههمریختگی چیدمان نمایش داده شوند.
ساخت این رابطها معمولاً یک موازنه بین سرعت توسعه و پایداری است. در حالی که Vercel AI SDK لولهکشی لازم برای استریم (Streaming) — یعنی ارسال تکهتکه دادهها همانطور که تولید میشوند، شبیه به پخش زنده یک ویدیو — را فراهم میکند، اما لایه فرانتاند جایی است که اکثر باگهای کاربر نهایی رخ میدهند. این موضوع بهویژه هنگام ادغام با کتابخانههایی مثل shadcn/ui مشهود است؛ جایی که تفاوت یک «دمو» با یک «محصول» در نحوه مدیریت استریمهای قطعشده و ورودیهای پیچیده تعریف میشود.
همانطور که در تحلیل قبلی ما دربارهی حفرههای امنیتی در Vercel AI SDK اشاره کردیم، توسعهدهندگان اکنون باید دغدغههای ایمنی را با نیازهای تجربه کاربری (UX) در تعاملات پرتکرار متوازن کنند. هدف، خلق سیستمی است که رابط کاربری آن صرفاً متن را نمایش ندهد، بلکه یک ماشین وضعیت (State Machine) پیچیده از افکار و اقدامات هوش مصنوعی را مدیریت کند.
معماری بخشهای پیام
به نقل از مستندات فنی SDK، رندر کردن پیامها به صورت رشتههای متصلشده باید متوقف شود. هوک useChat در Vercel AI SDK پیامهایی را برمیگرداند که از بخشهای مجزا تشکیل شدهاند: متن، استدلال، فراخوانی ابزارها با وضعیت مربوطه، منابع و فایلها. هر قابلیت در یک UI حرفهای به این معماری مبتنی بر بخشها وابسته است.
برای پیادهسازی این ساختار، از یک تابع نگاشت (Mapping) برای رندر کردن کامپوننتها بر اساس نوع بخش استفاده کنید:
- از
isToolUIPartدر بستهaiبرای شناسایی و رندر کامپوننتهای ابزار استفاده کنید. بخشهای ابزار با پیشوندtool-<name>تایپ میشوند که قابلاعتمادترین روش بررسی است. - تایپهای
textرا به یک کامپوننت Markdown متصل کنید. - تایپهای
reasoningرا به یک کامپوننت مجزای «در حال تفکر» (Thinking) منتقل کنید تا پاسخ اصلی تمیز بماند. - تایپهای
source-urlرا به کامپوننت منبع (Source) نگاشت کنید. - بلاکهای UI را از وارد کردن مستقیم SDK پاک کنید و بخشها را به عنوان Props پاس دهید. این کار باعث میشود اگر بعدها بکاند خود را به LangGraph یا سرور شخصی تغییر دادید، با تغییر فرمت خروجی به همان شکل (Shape)، کل UI شما بههم نریزد.
حل بحران ویرایشگر (Composer)
باکس متن (Textarea) خطاسازترین بخش هر اپلیکیشن چت است. یک نقطه شکست بحرانی، ویرایشگر روش ورودی (IME) است که برای زبانهای شرق آسیا (CJK) مانند ژاپنی، چینی یا کرهای استفاده میشود. وقتی کاربر برای تأیید یک نویسه (Character Candidate) کلید Enter را میزند، یک هندلر ساده و ناشیانه، کلمه نیمهتمام را ارسال میکند.
برای رفع این مشکل، توسعهدهندگان باید در هندلر onKeyDown مقدار event.nativeEvent.isComposing را بررسی کنند و اگر مقدار آن true بود، از ارسال پیام جلوگیری کنند. منطق باید اینگونه باشد: اگر کلید Enter نبود یا Shift نگه داشته شده بود، بازگرد؛ اگر isComposing فعال بود، بازگرد؛ در غیر این صورت، پیشفرض را متوقف کرده (prevent default) و پیام را ارسال کن.
علاوه بر مدیریت ورودی، ویرایشگر به چندین قابلیت پایداری نیاز دارد:
- سایز پویا: ورودی باید از یک خط تا حدود هشت ردیف رشد کند و سپس اسکرول شود. برای جلوگیری از لگ، تغییرات ارتفاع باید از طریق
scrollHeightدر یک layout effect اندازهگیری و مستقیماً روی گره DOM اعمال شود. هرگونهsetStateدر شنونده ورودی (Native Input Listener)، در بارهای کاری بالا باعث افت یا تأخیر در ثبت کلیدها میشود. - پایداری: پیشنویسها را با استفاده از یک تابع debounced و با کلید ID چت در
localStorageذخیره کنید. کاربران اغلب پرامپتهای طولانی خود را بر اثر رفرش تصادفی صفحه از دست میدهند و بهندرت این اتفاق را میبخشند. - صفبندی پیامها: اگر کاربر در حین پاسخ مدل، پیام جدیدی فرستاد، UI باید یا دکمه ارسال را غیرفعال کند یا پیام را در صف قرار دهد تا پس از پایان استریم ارسال شود. حذف بیصدای پیام، بدترین گزینه است اما هنوز پیشفرض بسیاری از پیادهسازیهاست.
- مدیریت پیوستها: آپلودها اغلب شکست میخورند. بهجای نمایش یک اعلان (Toast) گذرا که سریعاً ناپدید میشود، خطا را مستقیماً روی کارت پیوست همراه با دکمه تلاش مجدد (Retry) نشان دهید.
استریم و پایداری بصری
توکنها — تکههای کوچکی از متن که مدل تکهتکه تولید میکند — اغلب در دستههای ناهمگون میرسند و باعث لرزش متن میشوند؛ یعنی ابتدا هیچ اتفاقی نمیافتد، سپس دوازده کلمه یکباره ظاهر میشوند و دوباره توقف رخ میدهد. یک UI حرفهای این روند را با نمایش نویسهها با سرعتی ثابت که با رشد بافر شتاب میگیرد، نرم میکند تا رابط کاربری هرگز از استریم عقب نماند و در عین حال دچار پرش نشود.
رندر Markdown در حین استریم تله دیگری است: سینتکسهای نیمهتمام. اگر مدل **bold را بنویسد اما ستارههای پایانی را هنوز تولید نکرده باشد، رندرر ممکن است برای یک فریم نمادهای خام را نشان دهد. راهکار این است که سینتکسهای inline ناقص را تا زمان بسته شدن یا پایان استریم نگه دارید. بلوکهای کد نیز نیاز به دقت مشابه دارند؛ آنها را بهصورت افزایشی هایلایت کنید و هرگز با هر توکن جدید، کل بلوک را دوباره پردازش (Re-highlight) نکنید.
مدیریت وضعیت برای کاربران حرفهای
کاربران واقعی انتظار دارند بتوانند پاسخ را متوقف کنند، دوباره تلاش کنند یا پیامها را ویرایش کنند. اقدام «توقف» (Stop) باید پاسخ ناقص را با یک نشانگر «متوقف شد» (Stopped) روی صفحه باقی بگذارد بهجای آنکه آن را حذف کند، تا کاربر بداند پاسخ کامل نیست.
- تولید مجدد: دکمههای تلاش مجدد را مستقیماً به تابع
regenerate()متصل کنید. این باید یک دکمه قابل مشاهده روی پیام باشد، نه یک ژست (Gesture) پنهان. - شاخهبندی (Branching): ویرایش یک پیام قدیمی باید یک شاخه نسخهبندی ایجاد کند. هر دو پاسخ قدیمی و جدید باید وجود داشته باشند. یک سوئیچر کوچک مثل «۱ / ۲» روی پیام قرار دهید تا کاربر بتواند بین نسخهها جابهجا شود؛ قابلیتی که در ChatGPT و Claude رایج است اما در UIهای دستساز اغلب فراموش میشود.
- مدیریت خطا: پاسخهای شکستخورده باید دلیل مشخص و یک اقدام جایگزین ارائه دهند. پیام کلی «خطایی رخ داد» بدون دکمه اقدام، یک بنبست است.
اسکرول پیشرفته و ابزارها
رفتار اسکرول باید هوشمند باشد تا صفحه کاربر را «بکشد» (Yanking). UI باید توکنهای جدید را دنبال کند، اما فقط در صورتی که کاربر در پایین صفحه باشد. به محض اینکه کاربر به بالا اسکرول کرد، دنبال کردن خودکار باید متوقف شود و دکمه «پرش به آخرین پیام» همراه با تعداد پیامهای از دست رفته ظاهر شود. وقتی کاربر روی آن کلیک کرد، دنبال کردن دوباره فعال شود.
برای بارگذاری تاریخچه، ارتفاع اسکرول را قبل و بعد از افزودن پیامهای قدیمی اندازه بگیرید و scrollTop را در همان فریم با مقدار تفاضل تنظیم کنید تا لرزش بصری ایجاد نشود. همچنین، هنگام ارسال پیام، UI باید بهگونهای اسکرول شود که پیام کاربر در بالای دیدرس (Viewport) پین شود و پاسخ مدل در زیر آن استریم شود.
فراخوانی ابزارها (Tool Calls) به یک ماشین وضعیت دقیق نیاز دارد. AI SDK وضعیتهایی شامل input-streaming (در حال استریم ورودی)، input-available (ورودی موجود)، output-available (خروجی موجود)، output-error (خطای خروجی)، approval-requested (درخواست تأیید)، approval-responded (پاسخ به تأیید) و output-denied (خروجی رد شده) را ارائه میدهد. این قابلیتها زیربنای ساختارهای پیچیدهتری هستند، مشابه آنچه در پیادهسازی عاملهای هوشمند چندمرحلهای با Gemini مشاهده میکنیم که در آن فراخوانی توابع نقش کلیدی دارد.
- سلسلهمراتب بصری: خواندنها و جستوجوها را میتوان در یک خلاصه تکخطی جمع کرد، اما ویرایشها، دستورات و خطاها باید باز بمانند.
- تأییدیهها: اینها استریم را متوقف میکنند و به دکمههای واضح «اجازه بده» (Allow) و «رد کن» (Deny) نیاز دارند. راهی برای گفتن «بهجای آن، این کار را بکن» اضافه کنید که متن را به عنوان دلیل رد کردن به مدل بازگرداند.
- نتایج کاربردی: ویرایش فایل باید در یک Diff، دستورات در یک ترمینال و جستوجوها در لیست نتایج باز شوند. ابزاری که فقط میگوید «انجام شد»، کاربر را عادت میدهد که فراخوانی ابزارها را نادیده بگیرد.
تفکر و منابع
بخشهای استدلالی یا زنجیره تفکر (Chain-of-Thought) — مثل وقتی شاگرد ریاضی پای تخته بلند بلند فکر میکند تا به جواب برسد — باید قابلیت جمعشدن داشته باشند و بهصورت پیشفرض به محض شروع پاسخ نهایی، بسته شوند. در حالی که مدل در حال تفکر است، یک برچسب زنده که هر مرحله را دنبال میکند (مثلاً «در حال جستوجوی مستندات»، «در حال خواندن سه فایل») بسیار اطمینانبخشتر از یک اسپینر ساده است.
ارجاعات (Citations) باید بهصورت inline و در کنار ادعاهایی که پشتیبانی میکنند قرار گیرند، نه در لیستی در انتهای پیام که کاربران بهندرت میخوانند.
دسترسیپذیری و پرداخت نهایی
دسترسیپذیری (Accessibility) اغلب نادیده گرفته میشود اما برای محصول ضروری است. پیامهای جدید باید از طریق یک ناحیه با role="log" اعلام شوند تا کاربران صفحهخوان (Screen Reader) پاسخها را بشنوند. دکمه توقف باید با کیبورد قابل دسترسی باشد و هر دکمهای که فقط آیکون دارد، به یک aria-label نیاز دارد.
متنهای در حال استریم نباید با هر توکن یک اعلان live-region ایجاد کنند، زیرا صفحهخوانها را غیرقابل استفاده میکند. بهجای آن، اعلانها را فقط در مرز جملات یا پس از تکمیل پیام فعال کنید.
طبق گزارش منتشر شده، در ۱۶ اکتبر ۲۰۲۶، shadcn/ui مجموعهای به نام AI Elements را عرضه کرد که شامل ۲۱ الگوی آماده از جمله چت، ورودی پرامپت، استدلال و ابزار است. این الگوها قرار است جایگزین کتابخانه قدیمی Vercel AI Elements شوند که برای پایان نوامبر ۲۰۲۶ بازنشسته میشود. این بهروزرسانی در راستای بهینهسازی زیرساختها برای عصر کدنویسی هوش مصنوعی است، همانطور که HextaUI با متادیتای ماشینخوان تلاش کرد نرخ خطای عاملهای کدنویسی را کاهش دهد.
این چرخش به سمت کامپوننتهای ماژولار و مبتنی بر بخشها به این معناست که توسعهدهندگان دیگر مجبور نیستند «لولهکشی» یک رابط چت را از صفر بسازند. با تمرکز بر وضعیت هر بخش بهجای رشتههای خام، UI به بازتابی از منطق داخلی مدل تبدیل میشود.
برای کسانی که به دنبال نسخهای کاملاً اسمبلشده از این الگوها هستند، HextaUI بلوکهایی را ارائه میدهد که مدیریت ورودی CJK، صفبندی پیامها و شاخهبندی نسخهها را بهصورت پیشفرض پیاده کرده است. بخش Chat Thread آنها شامل توقف، تلاش مجدد و نسخههاست و Prompt Input آن رایگان است. شما میتوانید اپلیکیشن اسمبلشده را در hextaui.com/blocks/hexta-ai بررسی کنید.
توسعهدهندگان باید اکنون رابطهای چت خود را از نظر پشتیبانی از IME و لرزشهای استریم بازبینی کنند. مرز بعدی، ادغام این الگوهای UI با گردشکارهای عاملمحور (Agentic) است، جایی که تأیید ابزارها به نقطه اصلی تعامل تبدیل میشود.
گام بعدی شما
- بررسی هندلر
onKeyDownدر ورودیهای چت برای اطمینان از عدم ارسال پیامهای نیمهتمام در زبانهای CJK. - جایگزینی رندر رشتههای ساده با معماری Part-based برای تفکیک بخشهای استدلال و متن.
- پیادهسازی سیستم شاخهبندی (Branching) برای پیامهای ویرایششده جهت بهبود تجربه کاربری.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو