اگر رابط کاربری اپلیکیشن شما هنگام دریافت پاسخهای ساختاریافته از هوش مصنوعی دچار لرزش یا خطا میشود، دلیلش این است که پارسرهای استاندارد تا پیش از رسیدن آخرین براکت، هیچ دادهای را برنمیگردانند. کتابخانه fenced-json که در ۱۶ اکتبر ۲۰۲۶ منتشر شد، با تغییر نگاه به دادههای ناقص و تبدیل آنها به یک «وضعیت در حال تکامل» بهجای «خطای سینتکس»، این مشکل را ریشهای حل میکند.
این ابزار بدون هیچ وابستگی خارجی در محیط Node و مرورگر اجرا میشود و حتی یک محیط Playground برای تست زنده فراهم کرده است تا توسعهدهندگان ببینند فیلدها چگونه یکی پس از دیگری ظاهر میشوند. طبق گزارش نویسنده، اکثر برنامهنویسان پیش از این از توابع دستساز و شکننده استفاده میکردند که با کوچکترین تغییر در ساختار مدل یا اضافه شدن یک جمله توضیحی در ابتدای پاسخ، باعث کرش کردن فرانتاند میشدند.
هوش مصنوعی زاینده (Generative AI) — مثل هنرمندی که همزمان با کشیدن تابلو، آن را به شما نشان میدهد — بهندرت دادهها را بهصورت خالص ارسال میکند و معمولاً آنها را در حصارهای مارکداون (Markdown Fences) یا جملات تعارفی میپیچد. همانطور که در تحلیلهای قبلی ما دربارهی بهینهسازی خروجیهای مدلهای زبانی اشاره کردیم، مدیریت این «نویزهای» متنی یکی از بزرگترین چالشهای توسعهدهندگان است.
مدیریت مقدمهها و حصارهای متنی
fenced-json منطق دقیقی برای تشخیص نقطه شروع دادهها دارد. این پارسر ابتدا تمام متون ادبی را نادیده میگیرد و منتظر میماند تا یک حصار مارکداون و یک خط جدید (Newline) ظاهر شود. به نقل از مستندات این ابزار، اگر مدل پاسخی مثل Sure! jsonارسال کند، وضعیتempty` برمیگردد چون تا رسیدن خط جدید، نمیتوان تگ زبان را از محتوا تشخیص داد.
اگر حصاری وجود نداشته باشد، پارسر با اولین براکت { یا [ فعال میشود. البته توسعهدهندگان میتوانند با تنظیم fences: false این رفتار را غیرفعال کنند تا هر متنی پیش از JSON به عنوان خطا تلقی شود. هر چیزی که بعد از آخرین براکتی بسته شود (مثل جملات پایانی مدل)، در فیلد rest ذخیره میشود تا برنامهنویس تصمیم بگیرد آن را نمایش دهد یا حذف کند.
منطق مقادیر ناقص
یکی از کلیدیترین تصمیمات فنی در این بسته، نحوه برخورد با انواع دادههای ناقص است. نویسنده یک ماتریس تصمیمگیری برای این موارد طراحی کرده است:
رشتهها (Strings): بهصورت پیشفرض، رشتههای ناقص نمایش داده میشوند چون برای کاربر مفید است. اما برای مقادیر Enum یا شناسهها، گزینه
partialStrings: 'drop'وجود دارد تا رشته تنها پس از بسته شدن کوتیشن نمایش داده شود.اعداد (Numbers): اعداد ناقص بهطور پیشفرض حذف میشوند. نمایش عدد
۱۲در حالی که مقدار نهایی۱۲۵است، از نظر منطقی غلط است و باعث گمراهی کاربر میشود.مقادیر ثابت (Literals): مقادیر Boolean و null تنها زمانی بازگردانده میشوند که بهطور کامل تکمیل شده باشند.
کانتینرها: اشیاء و آرایهها همزمان با رشد، بازگردانده میشوند و پارسر بهطور خودکار آنها را میبندد تا ساختار درختی معتبر باقی بماند.
توالیهای Escape: این ابزار بهطور صحیح مواردی مثل
\u00را که بین دو تکه (Chunk) تقسیم شدهاند مدیریت میکند.
ردیابی مسیر استریم
برای جلوگیری از رندر زودهنگام المانهای UI، این پارسر مسیر دقیق استریم را ردیابی میکند. این قابلیت برای درختهای پیچیده حیاتی است؛ مثلاً یک لیست پیشنهادات نباید آیتمبهآیتم رندر شود، بلکه باید پس از تکمیل هر آیتم نمایش یابد.
در ساختاری مثل {"a": "x", "b": [1, {"c": "d مسیر استریم ["b", 1, "c"] است. توسعهدهندگان میتوانند با تابع isComplete بررسی کنند که آیا یک فیلد خاص بهطور کامل رسیده است یا خیر. این یک بررسی تکخطی است که از رندر شدن دادههای ناقص در بخشهای حساس اپلیکیشن جلوگیری میکند.
عملکرد و معماری
برخلاف ابزارهایی مثل fixJson در Vercel AI SDK که از ماشین حالت افزایشی استفاده میکنند، fenced-json در هر تکه جدید، کل رشته انباشته شده را دوباره پارس میکند. این تصمیم برای تضمین «تغییرناپذیری» (Immutability) نتایج گرفته شده است تا مقادیر در حین اجرا تغییر نکنند.
بر اساس بنچمارکهای انجام شده روی لپتاپهای سری M با Node 23.11، هزینه این پردازش برای پاسخهای معمولی ناچیز است:
- سند ۱ کیلوبایتی: ۰.۲ میلیثانیه در مجموع
- سند ۱۶ کیلوبایتی: ۳۳ میلیثانیه در مجموع
- سند ۶۴ کیلوبایتی: ۵۲۳ میلیثانیه در مجموع
از آنجا که هزینه هر تکه حتی در حجم ۶۴ کیلوبایت بسیار کمتر از یک فریم انیمیشن است، این پیچیدگی محاسباتی قابل قبول است.
مقایسه با ابزارهای موجود
نویسنده این ابزار را با چهار بسته دیگر از جمله jsonrepair و partial-json مقایسه کرده است. اکثر آنها در مدیریت حصارهای مارکداون یا مقدمههای متنی شکست خوردند. در حالی که jsonrepair در اصلاح JSONهای خراب عالی است، اما قابلیت ردیابی مسیر استریم برای UIهای پیشرفته را ندارد.
جزئیات پیادهسازی
این بسته سه خروجی اصلی parsePartial ،PartialJson و isComplete را ارائه میدهد. استفاده از تایپ DeepPartial<T> در TypeScript باعث میشود توسعهدهنده مجبور شود تمام حالتهای undefined را مدیریت کند و از باگهای رندرینگ جلوگیری شود.
گام بعدی شما
- اگر از استریم JSON استفاده میکنید، بسته
fenced-jsonرا جایگزین توابع Regex دستی کنید. - برای فیلدهای حساس (مثل قیمت یا ID)، گزینه
partialNumbers: 'drop'را فعال کنید تا دادههای غلط نمایش داده نشود. - از تابع
isCompleteبرای مدیریت زمان رندر شدن کامپوننتهای پیچیده در فرانتاند استفاده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو