پرش به محتوای اصلی
پرش به محتوای مقاله

درون مکانیسم NIM؛ تبدیل متون پیش‌بینی‌ناپذیر به فرمت‌های سخت‌افزاری

·۱۶ تیر ۱۴۰۵۹ دقیقه مطالعه
راهنما
خروجی ساختاریافته با NVIDIA NIM: بازگرداندن داده به جای متن توسط عامل هوشمند
خروجی ساختاریافته با NVIDIA NIM: بازگرداندن داده به جای متن توسط عامل هوشمند
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

معرفی یک الگوریتم بازپشتی (Feedback Loop) چهارمرحله‌ای در سمت کلاینت برای اصلاح خودکار JSONهای معیوب، به جای تکیه صرف بر تنظیمات API سرور.

وقتی یک مدل زبانی بزرگ (LLM) — مثل کتابخانه‌داری که میلیاردها صفحه را خوانده و حالا با همان لحن کتاب‌ها جواب می‌دهد — به جای یک شیء داده‌ای، یک پاراگراف متن برمی‌گرداند، رابط کاربری شما می‌شکند، تست‌های نرم‌افزاری شکست می‌خورند و API شما به بازی حدس زدن با عبارات منظم (Regex) تبدیل می‌شود. برای هشت بخش از این سری، عامل هوشمند هر نوبت را به یک شکل به پایان می‌رساند: چاپ یک جمله برای خواندن توسط انسان. در حالی که این روش برای یک نمایش (Demo) کاربردی است، اما برای یک محصول واقعی یک بن‌بست است؛ زیرا عامل‌های هوشمند قابل‌اعتماد نمی‌توانند تنها با تکیه بر نثر (Prose) بقا یابند. برای حل این مشکل، «بی توکیان» (B Torkian)، قهرمان توسعه‌دهنده انویدیا در دانشگاه USC، در بخش نهم این سری، مکانیزمی را برای انتقال خروجی‌های عامل از متن‌های غیرقابل پیش‌بینی به یک «قرارداد JSON» اعتبارسنجی‌شده تشریح کرده است.

به گزارش dev.to، اکثر توسعه‌دهندگان سعی می‌کنند از طرحواره‌های (Schemas) response_format در سمت سرور برای اجبار مدل به رعایت ساختار استفاده کنند. اما این روش در مدل‌های وزن‌باز (Open Weights) — یعنی مدل‌هایی که دستور پخت آن‌ها علناً منتشر شده و نه فقط غذای آماده — مانند llama-3.3-70b در کاتالوگ‌های API میزبانی‌شده، غیرقابل‌اعتماد است. حالت «طرحواره سخت‌گیرانه» (Strict schema mode) یک ویژگی سمت سرور است که نقاط انتهاییِ مدل‌های باز، آن را به‌طور ناسازگاری پیاده‌سازی می‌کنند و اغلب با فراخوانی ابزارها (Tool Calling) تداخل دارد. وضعیت فعلی در سطح استانداردهای تولیدی (Production) ایجاب می‌کند که مرز اعتماد از سرور به سمت کلاینت منتقل شود: یعنی درخواست JSON در پرامپت ارسال شود و سپس فرآیند تجزیه، اعتبارسنجی و اصلاح به‌صورت دستی انجام گیرد.

خط لوله خروجی ساختاریافته

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

  • تجزیه (Parsing): تابع parse_json_object با استفاده از متدهای .find() و .rfind()، اولین علامت آکولاد باز { و آخرین علامت آکولاد بسته } را در متن شناسایی می‌کند. این کار باعث می‌شود متون اضافی گفتگو یا فنس‌های Markdown (مانند ```json) حذف شوند تا فقط شیء JSON ایزوله شود. اگر هیچ آکولادی یافت نشود یا آکولاد بسته قبل از آکولاد باز بیاید، یک ValueError با پیام «هیچ شیء JSON یافت نشد» صادر می‌شود.
  • اعتبارسنجی (Validation): تابع validate_answer شیء تجزیه‌شده را با مجموعه‌ای سخت‌گیرانه از کلیدهای ضروری (REQUIRED_KEYS) بررسی می‌کند: status ،answer ،category ،items ،missing و sources. این تابع تأیید می‌کند که status و category اعضای لیست‌های تعریف‌شده (Enums) باشند. به‌طور مشخص، STATUSES باید یکی از مقادیر {"answered", "not_found", "needs_clarification"} و CATEGORIES باید یکی از مقادیر {"campus_event", "campus_hours", "campus_resource", "comparison", "refusal"} باشد. همچنین بررسی می‌کند که answer یک رشته متنی باشد و items ،missing و sources همگی لیست باشند. به‌ویژه، چک می‌کند که هر ورودی در لیست items خود یک شیء (Object) باشد.
  • اصلاح (Repair): اگر اعتبارسنجی شکست بخورد، سیستم دقیقاً یک تلاش برای اصلاح از طریق repair_answer_json انجام می‌دهد. در این مرحله، لیست خطاهای مشخص به مدل بازگردانده می‌شود در حالی که دما (Temperature) روی صفر و محدودیت توکن‌ها (max_tokens) روی ۸۰۰ تنظیم شده است. پرامپت سیستمی در این مرحله این است: «تو اصلاح‌کننده JSONهای معیوب هستی. فقط و فقط یک شیء JSON معتبر برگردان.» پرامپت کاربر نیز از مدل می‌خواهد تمام حقایق را حفظ کرده و فقط ساختار JSON را اصلاح کند. اگر API خطا دهد یا نتیجه همچنان غیرقابل تجزیه باشد، تابع اصلاح مقدار None را برمی‌گرداند.
  • جایگزین (Fallback): اگر اصلاح شکست بخورد یا نتیجه همچنان نامعتبر باشد، عامل یک شیء خطای قطعی (Deterministic) را از طریق format_error برمی‌گرداند. این کار از کرش کردن برنامه جلوگیری کرده و پاسخی تایپ‌شده برمی‌گرداند که در آن status برابر با needs_clarification ، answer برابر با «من نتوانستم یک پاسخ ساختاریافته معتبر تولید کنم. لطفاً دوباره بپرسید.» و category برابر با refusal است. در مخزن کامل کد، این جایگزین می‌تواند یک پاسخ اختیاری بر اساس علت خطا (مثلاً برای شکست در محدودیت تعداد گام‌ها) بگیرد تا حتی مسیر «تسلیم شدن» نیز قرارداد داده‌ای را رعایت کند.

تعریف قرارداد داده‌ها

برای یک عامل دستیار دانشگاهی، توکیان یک قرارداد شش‌کلیدی تعریف کرده است تا هر نوع تعاملی را مدیریت کند. این قرارداد مانند یک لولا عمل می‌کند: لاگ‌های ردیابی (Tracing) این شیء را ثبت می‌کنند، ارزیابی‌ها (Evaluations) روی فیلدهای آن ادعاهایی را بررسی می‌کنند و استقرار نهایی (Deployment) آن را به عنوان بدنه پاسخ HTTP برمی‌گرداند:

  • status: یک Enum شامل answered (پاسخ داده شد)، not_found (یافته نشد) یا needs_clarification (نیاز به شفاف‌سازی) تا کدهای پایین‌دستی بتوانند به‌صورت منطقی تصمیم‌گیری کنند.
  • answer: یک رشته متنی به زبان طبیعی (مثلاً: «باشگاه هوش مصنوعی USC هر پنجشنبه ساعت ۵ عصر در ساختمان مهندسی، اتاق ۲۰۴ تشکیل جلسه می‌دهد.»).
  • category: یک Enum متشکل از campus_event (رویداد دانشگاهی)، campus_hours (ساعات کاری)، campus_resource (منابع دانشگاهی)، comparison (مقایسه) یا refusal (رد درخواست).
  • items: یک لیست از اشیاء قابل‌خوان برای ماشین (Machine-readable payload) که شامل جزئیات ساختاریافته مانند name (نام)، day (روز)، time (ساعت) و location (مکان) است (مثلاً: {"name": "USC AI Club meeting", "day": "Thursday", "time": "5 PM", "location": "engineering building, room 204"}).
  • missing: لیستی از موارد مشخصی که کاربر خواسته بود اما عامل نتوانست آن‌ها را بیابد.
  • sources: خطوط دقیقاً استخراج‌شده از پایگاه دانش برای مبنی‌سازی (Grounding) پاسخ، تا روحیه فصل «نرده‌های حفاظتی» (Guardrails) حفظ شود.

یکپارچگی با NVIDIA NIM

در حالی که این راهنما بر اعتبارسنجی سمت کلاینت تأکید دارد، اشاره می‌کند که نسخه‌های خودمیزبانی‌شده NVIDIA NIM 1.x از رمزگشایی محدودشده (Constrained Decoding) در سمت سرور پشتیبانی می‌کنند. کاربران می‌توانند از پارامتر extra_body={"nvext": {"guided_json": schema}} برای رمزگشایی محدود استفاده کنند و آخرین نسخه‌ها در حال حرکت به سمت حالت‌های JSON سازگار با OpenAI هستند. صرف‌نظر از این قابلیت‌های سرور، مستندات رسمی انویدیا (در دسترس در https://docs.nvidia.com/nim/large-language-models/latest/structured-generation.html) صراحتاً به توسعه‌دهندگان توصیه می‌کند که پاسخ را در سمت کلاینت اعتبارسنجی کنند، که دقیقاً همان «نردبان» است که در این پست ساخته شد.

برای پیاده‌سازی این سیستم، منطق داخلی عامل بازطراحی شده است. پیش از این، در ورکشاپ ۸، دو حلقه تقریباً یکسان در توابع chat() و stream() وجود داشت. این‌ها اکنون در یک حلقه مشترک به نام _run_turn(user_message, stream) تجمیع شده‌اند. این حلقه مشترک از _complete(stream) برای مدیریت فراخوانی مدل، بازسازی ابزارها و قطعات پیام استفاده می‌کند. اکنون chat() و stream() تنها به عنوان واسطه‌های تک‌خطی برای _run_turn عمل می‌کنند:

def chat(self, user_message: str) -> dict:
    return self._run_turn(user_message, stream=False)

def stream(self, user_message: str) -> dict:
    return self._run_turn(user_message, stream=True)

یک نقطه ضعف صادقانه در این معماری این است که نمایش زنده توکن‌به-توکن برای پاسخ نهایی حذف شده است. زیرا نمایش یک JSON نیمه‌کاره برای کاربر بی‌معنی است و UI باید منتظر تکمیل کامل شیء بماند. با این حال، استریمینگ در سطح شبکه (Wire-level streaming) و بازسازی قطعات فراخوانی ابزار از بخش ۸ همچنان پابرجاست.

آموزش فرمت به مدل

هدایت ابزارها از ورکشاپ‌های ۷ و ۸ بدون تغییر باقی مانده است، اما بلوکی با عنوان FINAL ANSWER FORMAT به انتهای پرامپت سیستمی اضافه شده است. این بلوک صراحتاً دستور می‌دهد که پاسخ نهایی «باید» یک شیء JSON واحد باشد و هیچ چیز دیگری نباشد — نه نثر و نه کد-فنس‌ها. این بخش کلیدهای دقیق و مقادیر مجاز Enum برای status و category را لیست می‌کند تا قرارداد تثبیت شود:

«فرمت پاسخ نهایی (FINAL ANSWER FORMAT): وقتی استفاده از ابزارها را به پایان رساندی، پاسخ نهایی تو باید یک شیء JSON واحد باشد و هیچ چیز دیگر — نه نثر و نه کد-فنس. دقیقاً از این کلیدها استفاده کن: status (answered|not_found|needs_clarification), answer (string), category (campus_event|campus_hours|campus_resource|comparison|refusal), items (list), missing (list), sources (list).»

به محض اینکه مدل فراخوانی ابزارها را متوقف کند، عامل متد _finalize_json را فراخوانی می‌کند. این متد سعی می‌کند متن خام را تجزیه و اعتبارسنجی کند؛ اگر شکست بخورد، تلاش برای اصلاح را فعال می‌کند. این فرآیند از یک جریان سخت‌گیرانه پیروی می‌کند: «تجزیه $\rightarrow$ اعتبارسنجی $\rightarrow$ اصلاح (در صورت نیاز) $\rightarrow$ جایگزین نهایی». نکته حیاتی این است که عامل، نسخه استاندارد json.dumps(data) را در تاریخچه گفتگو ذخیره می‌کند، نه متن خام مدل را. این کار تضمین می‌کند که حافظه برای نوبت بعدی تمیز و ساختاریافته باقی بماند.

آزمایش پیاده‌سازی

اثربخشی این رویکرد هنگام اجرای عامل در مواجهه با پرس‌وجوهای پیچیده با استفاده از ChatSession(verbose=True) مشهود است. برای سوالی مانند «باشگاه هوش مصنوعی USC چه زمانی تشکیل جلسه می‌دهد؟»، عامل وضعیتی با answered و دسته‌بندی campus_event برمی‌گرداند. وقتی پرسیده شود «چند روز تا آن زمان مانده است؟»، عامل از حافظه چند-نوبتی و ابزارها برای تحلیل ضمیر «آن» استفاده می‌کند. در مورد سوال «کدام زودتر است، آن جلسه یا ساعات اداری AI/ML؟»، عامل برای هر روز یک بار فراخوانی ابزار مقایسه را اجرا می‌کند تا نتیجه را تعیین کند.

زمانی که عامل به بن‌بست می‌رسد — مثلاً وقتی رمز عبور وای‌فای دانشگاه (که اطلاعاتی محدود است) از او خواسته می‌شود — او صرفاً در قالب نثر عذرخواهی نمی‌کند. به جای یک جمله مبهم، یک شیء refusal تایپ‌شده برمی‌گرداند:

{"status": "not_found", "answer": "من این اطلاعات را ندارم — لطفاً از باشگاه هوش مصنوعی USC بپرسید.", "category": "refusal", "items": [], "missing": ["USC campus wifi password"], "sources": []}.

انتقال از دمو به محصول

این تغییر، عامل را از یک «چت‌بات» به یک «قطعه نرم‌افزاری» تبدیل می‌کند. با تبدیل پاسخ نهایی به داده، عامل قابل یکپارچگی با لاگ‌های ردیابی، مجموعه‌های ارزیابی خودکار و پاسخ‌های استاندارد API HTTP می‌شود. در بستر کلی این سری، این نقطه چرخش است. در حالی که ورکشاپ‌های ۱ تا ۸ بر هوشمندتر کردن عامل تمرکز داشت، ورکشاپ ۹ آن را «قابل‌اتصال» (Integratable) می‌کند:

  • ورکشاپ ۱: مغز (Brain)
  • ورکشاپ ۲: حافظه حقایق (Memory of facts)
  • ورکشاپ ۳: قضاوت (Judgment)
  • ورکشاپ ۴: قابلیت انتقال (Portability)
  • ورکشاپ ۵: دست‌ها (Hands / Tool use)
  • ورکشاپ ۶: یک برنامه (A plan)
  • ورکشاپ ۷: حافظه گفتگو (Conversation memory)
  • ورکشاپ ۸: صدای آنی (Real-time voice/streaming)
  • ورکشاپ ۹: یک قرارداد (خروجی‌ای که نرم‌افزارهای دیگر بتوانند مصرف کنند)

این بنیاد ساختاری، مسیر نیمه دوم سری را هموار می‌کند:

  • ردیابی‌ها (Traces): ثبت ابزارها، تأخیر و شیء JSON نهایی هر نوبت به صورت JSONL برای تحلیل‌های بازنگرانه و مشاهده دقیق کارهای عامل.
  • ارزیابی‌ها (Evals): بازپخش سوالات و بررسی اینکه آیا status و category صحیح هستند و آیا فیلد missing در سوالاتی مانند رمز وای‌فای به‌درستی فعال می‌شود.
  • استقرار (Deployment): ماندگارسازی تاریخچه‌ها و سرویس‌دهی بدنه JSON پشت یک API تولیدی.

توسعه‌دهندگان می‌توانند پیاده‌سازی کامل را در مخزن گیت‌هاب github.com/torkian/nvidia-nim-workshop بیابند، شامل نوت‌بوک تک-کلیکی Colab با نام part9_structured_output.ipynb و اسکریپت پایتون part9_structured_output.py. این کد تحت لایسنس MIT است و به توسعه‌دهندگان اجازه می‌دهد آن را فورک کرده و پایگاه دانش و قرارداد را با نیازهای پروژه خود جایگزین کنند. این سری کامل از برنامه‌های پایه NIM و RAG مبتنی بر Embedding (بخش ۲)، به نرده‌های حفاظتی (بخش ۳)، خودمیزبانی NIM (بخش ۴)، فراخوانی ابزارهای چندمرحله‌ای (بخش ۵-۶)، حافظه گفتگو (بخش ۷) و استریمینگ (بخش ۸) پیش می‌رود.

گام بعدی شما

  • پیاده‌سازی حلقه parse-validate-repair را در پروژه‌هایی که از مدل‌های Llama-3.1 یا 3.3 استفاده می‌کنند، جایگزین تکیه بر response_format در سمت سرور کنید.
  • برای کاهش نرخ خطا، در مرحله Repair، مقدار Temperature را روی ۰ تنظیم کنید تا خروجی مدل متمرکزتر و دقیق‌تر شود.
  • ساختار خروجی‌های مدل خود را به صورت یک قرارداد (Contract) تعریف کنید تا بتوانید داشبوردهای مانیتورینگ را روی فیلدهای خاص (مانند status برای تحلیل نرخ شکست) متصل کنید.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell مراجعه کنید.

چرا این موضوع مهم است؟

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

تأثیر برای ایران

این متد برای توسعه‌دهندگان ایرانی که از مدل‌های وزن‌باز (Open Weights) روی سرورهای شخصی یا ابری استفاده می‌کنند، راهکار عملی برای حذف خطاهای Runtime در اپلیکیشن‌های عامل‌محور است.

·نگاه ما
تحریریه دات‌هوش

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

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.