تصور کنید در میانه تولید یک ویدیوی گرانقیمت، اتصال اینترنت کاربر قطع شود، یک خطای Gateway Timeout رخ دهد یا کاربر در میانه فرآیند تولید، مرورگر خود را رفرش کند. این لحظات «مسیرهای ناخوشایند» (Unhappy Path)، چالش واقعی مهندسی در ساخت محصولات تجاری در مقیاس تولید هستند و دشواری آنها را بسیار بیشتر از یک درخواست ساده API به ارائهدهندگان هوش مصنوعی است.
به نقل از راهنمای فنی منتشر شده در dev.to در ۲۰ اوت ۲۰۲۶، شکاف میان یک نمونه اولیه (Prototype) و یک محصول قابلاعتماد، با ایجاد یک مدل بازیابی پر میشود که هویت تسک و وضعیت عدم قطعیت را حفظ کند. بسیاری از توسعهدهندگان تصور میکنند یک تسک یا در انتظار است، یا موفق و یا شکستخورده؛ اما سامانههای صنعتی باید وضعیت «نامشخص» (Unknown) را هم بشناسند تا از خطاهای هزینهبر جلوگیری کنند.
گردشکار پایه
در یک سطح کلی، یک درخواست تولید نامتقارن (Asynchronous) توالی خاصی را دنبال میکند: کلاینت $\rightarrow$ API تولید $\rightarrow$ رکورد محلی تسک $\rightarrow$ ارائهدهنده AI $\rightarrow$ نظارت (Polling) یا وبهوک $\rightarrow$ تطبیق نتیجه $\rightarrow$ نتیجه پایدار.
در «مسیر خوشبینانه»، همه چیز ساده است: اعتبارسنجی درخواست، محاسبه هزینه، کسر اعتبار، ارسال تسک به ارائهدهنده، نظارت تا زمان موفقیت و در نهایت ذخیره نتیجه. اما در دنیای واقعی، سامانههای تولید به ندرت در این مسیر باقی میمانند. مشکلات جالب توجه دقیقاً در شکافهای بین این مراحل ظاهر میشوند. این چالشها اغلب زمانی تشدید میشوند که عاملهای هوش مصنوعی در درک دقیق ساختار APIها دچار خطا شوند، موضوعی که ابزار متنباز Scout برای شناسایی و رفع توهمات این عاملها در اتصال به APIها توسعه یافته است.
سناریویی را تصور کنید که در آن سرور درخواستی را به یک ارائهدهنده تولید ویدیو ارسال میکند. ارائهدهنده تسک را شروع میکند، اما پاسخ به دلیل تایماوت گم میشود. اگر سیستم این اتفاق را یک شکست تلقی کرده و دوباره تلاش کند، کاربر برای یک ویدیو دو بار هزینه میپردازد. اگر هم بدون تایید، آن را موفقیت بداند، ممکن است کاربر هرگز نتیجه را دریافت نکند.
رویکرد ماشینحالت
برای حل این مشکل، نویسنده پیشنهاد میکند با هر تسک تولید مانند یک ماشینحالت (State Machine) — شبیه به یک دستگاه فروش خودکار که دقیقاً میداند در هر لحظه در چه مرحلهای است و چه اتفاقی باید بیفتد — برخورد شود. به جای نتایج دوتایی (موفق/شکست)، سیستم باید وضعیت submission_unknown را ردیابی کند. این کار اجازه میدهد برنامه بین «رد درخواست» و «ارسال نامشخص» تفاوت قائل شود و از بازپرداختهای زودهنگام یا تسکهای تکراری جلوگیری کند.
یک مدل مفهومی برای این وضعیت میتواند به این شکل باشد:type GenerationState = | 'created' | 'submitting' | 'pending' | 'succeeded' | 'failed' | 'submission_unknown';
با حفظ این عدم قطعیت به جای اجبار هر نتیجه به حالت موفق یا شکست، سیستم میتواند تایماوتهای درگاه را بدون ریسک پرداخت مضاعف یا ایجاد تسکهای یتیم (Orphaned) در سمت ارائهدهنده مدیریت کند.

تضمین همتوانسازی (Idempotency)
رفتارهای کاربر مثل دوبار کلیک کردن روی دکمهها یا قطع اتصال در موبایل، اغلب منجر به درخواستهای تکراری میشود. مرورگرها ممکن است درخواستها را به طور خودکار دوباره ارسال کنند و وضعیت رابط کاربری (UI) اغلب پس از رفرش بازسازی میشود. راهکار پیشنهادی، پیادهسازی یک کلید همتوانسازی (Idempotency Key) پایدار برای هر ارسال است.
- ساختار درخواست: کلاینت یک کلید منحصربهفرد
idempotencyKeyرا همراه با پرامپت و مدل ارسال میکند (مثلاً:type GenerateRequest = { prompt: string; model: string; idempotencyKey: string; };). - جستوجوی سرور: سرور از این کلید برای یافتن یک تسک محلی برای یک کاربر خاص استفاده میکند:
const existingTask = await findTaskByIdempotencyKey({ userId, idempotencyKey });. - جلوگیری از Race Condition: به دلیل احتمال ارسال همزمان دو درخواست که هر دو از مرحله جستوجوی اولیه عبور کنند، محدودیتهای یکتایی (Uniqueness Constraints) در پایگاهداده ضروری است. جریان امن این است که ابتدا تسک موجود بررسی شود، سپس تلاش برای ایجاد آن با کلید یکتا صورت گیرد و اگر درج در دیتابیس به دلیل رقابت (Race) شکست خورد، تسک برنده بارگذاری و بازگردانده شود.
این مکانیزم تضمین میکند که اعتبار کاربر برای یک درخواست منطقی، هرگز دو بار کسر نشود و یک قلاب (Hook) برای بازیابی در کل گردشکار فراهم میکند.
مشاهده در مقابل چرخه حیات تسک
یک اشتباه رایج، یکی دانستن «تایماوت نظارت» با «شکست تسک» است. نظارت (Polling) — شبیه به کسی که هر چند دقیقه یکبار سرش را بیرون میآورد تا ببیند غذا آماده شده یا نه — صرفاً یک مکانیسم مشاهده است، نه خودِ تسک. ممکن است مرورگر بعد از ۳۰ ثانیه نظارت را متوقف کند، اما تسک در سمت ارائهدهنده همچنان به درستی در حال اجرا باشد.
توسعهدهندگان باید مکانیسم مشاهده را از خودِ تسک جدا کنند. این تمایز حیاتی است: «پایان پنجره نظارت $\neq$ شکست تسک تولید». با حفظ ID اصلی تسک، رابط کاربری میتواند به کاربر اطلاع دهد که نظارت متوقف شده است، بدون اینکه به اشتباه ادعا کند تولید شکست خورده است.
این امر اجازه میدهد کاربر پس از رفرش صفحه یا تعویض دستگاه، ردیابی همان تسک را با استفاده از یک تابع بازگشت (Resume) از سر بگیرد:async function resumeTask(taskId: string) { const localTask = await loadTask(taskId); if (isTerminal(localTask.status)) { return localTask; } return observeExistingTask(taskId); }
منطق تطبیق مشترک (Shared Reconciliation)
تسکها میتوانند از مسیرهای مختلفی به وضعیت نهایی (Terminal State) برسند: نظارت مستقیم در پیشزمینه، وبهوکهای ارائهدهنده، اسکریپتهای بازیابی زمانبندی شده یا عملیات تعمیر توسط ادمین. برای جلوگیری از Race Condition، تمام این مسیرها باید یک تابع تطبیق واحد و همتوان (Idempotent) را فراخوانی کنند.
این تابع مشترک، انتقال نهایی را مدیریت میکند:
- بارگذاری تسک فعلی.
- نادیده گرفتن تسکهایی که قبلاً نهایی شدهاند.
- ذخیره نتیجه یا شکست دقیقاً یکبار.
- بازپرداخت اعتبار حداکثر یکبار.
- بازگرداندن وضعیت محلی معیار (Canonical).
بدون این تطبیق مشترک، ممکن است یک وبهوک تسک را کامل کند در حالی که نظارت پیشزمینه هنوز فعال است و این منجر به نتایج یا بازپرداختهای تکراری شود. با بررسی اینکه آیا finalizedAt قبلاً مقداردهی شده است یا خیر، سیستم تضمین میکند که تنها یک مشاهدهگر میتواند تسک را نهایی کند، فارغ از اینکه چند سیگنال دریافت شود. محدودیتهای دیتابیس یا بهروزرسانیهای تراکنشی (Transactional) باید این اصل را تضمین کنند.
مرجعیت صورتحساب در سمت سرور
در حالی که فرانتاند هزینههای احتمالی را نمایش میدهد، سرور باید تنها مرجع نهایی برای صورتحساب باشد. هزینههای تولید پیچیده هستند و به متغیرهای متعددی بستگی دارند:
- مدل خاص مورد استفاده.
- مدت زمان ویدیو.
- رزولوشن خروجی.
- تعداد خروجیها.
- حالت تولید و قابلیتهای اختیاری.
سرور باید پارامترهای واقعی درخواست را تجزیه و اعتبارسنجی کرده و سپس هزینه مرجع را محاسبه کند: const creditCost = calculateCreditCost({ model: options.model, duration: options.duration, resolution: options.resolution, outputCount: options.outputCount });.
محاسبه هزینهها در سرور از ایجاد شکاف بین قیمت نمایشدادهشده و مبلغ کسرشده (به دلیل کلاینتهای قدیمی یا درخواستهای دستکاریشده) جلوگیری میکند. به همین ترتیب، بازپرداختها باید به یک تسک منحصربهفرد و دلیل تراکنش متصل باشند تا از بازپرداخت مکرر یک تسک در اثر تلاشهای بازیابی جلوگیری شود.
حفظ قصد کاربر (User Intent)
در نهایت، سیستم باید بیش از ID ارائهدهنده را ذخیره کند. برای بازسازی تجربه کاربر پس از کرش یا رفرش، پایگاهداده محلی باید پرامپت اصلی، مدل انتخابی، داراییهای ورودی (Assets)، تنظیمات تولید، ID محلی تسک، وضعیت فعلی و متادیتای نتیجه را ذخیره کند.
این جداسازی باعث میشود رکورد محلی، منبع حقیقت (Source of Truth) باشد. وضعیت ارائهدهنده به عنوان یک شواهد خارجی تلقی میشود که برای بهروزرسانی رکورد محلی به کار میرود؛ این کار مانع از نشت IDهای داخلی ارائهدهنده و جزئیات پیادهسازی آنها به رابط کاربری میشود.
تکامل تفکر مهندسی
این تغییر دیدگاه، تمرکز را از «انتقال داده» (Transport) — یعنی صرفاً ارسال و پرسوجوی تسکها — به یک معماری سهلایه منتقل میکند:
۱. انتقال ارائهدهنده: ارسال و پرسوجوی تسکهای خارجی.
۲. گردشکار محصول: مدیریت هویت تسک، بازیابی، صورتحساب و تطبیق.
۳. تجربه کاربر: توضیح شفاف پیشرفت، عدم قطعیت، شکست و بازیابی.
API ارائهدهنده فقط لایه اول را حل میکند؛ محصول واقعی در لایههای دوم و سوم ساخته میشود. برای جلوگیری از تبدیل این پیچیدگیها به بدهی فنی، استفاده از قراردادهای معماری به عنوان راهکاری برای کنترل کدهای تولید شده توسط هوش مصنوعی توصیه میشود تا ساختار سیستم در طول زمان حفظ شود. در حالی که ابزارهای AI میتوانند سرعت پیادهسازی را افزایش دهند، اما ممکن است توسعهدهندگان را وسوسه کنند تا پیش از قابلاعتماد شدن مدل وضعیت (State Model)، ویژگیهای جدید اضافه کنند. ارزشمندترین بخش کار، اضافه کردن مدل جدید نیست، بلکه تعریف این است که وقتی چندین چیز همزمان شکست میخورند، چه حقایقی باید ثابت بمانند.
برای کسانی که ابزارهای AI نامتقارن میسازند، محصول واقعی همان «مدل بازیابی» است. قابلیت اطمینان از این میآید که تضمین شود هر ارسال منطقی به یک تسک نگاشت شود، نتایج نامشخص با احتیاط مدیریت شوند و هر مسیر تکمیل، از یک قرارداد تطبیق همتوان مشترک استفاده کند.
گام بعدی شما
- وضعیتهای دوتایی (موفق/شکست) را در دیتابیس خود با وضعیتهای میانی مثل
submittingوunknownجایگزین کنید. - برای هر درخواست تولید، یک
idempotencyKeyاز سمت کلاینت دریافت و در سطح دیتابیس روی آن Unique Constraint بگذارید. - منطق کسر اعتبار و بازپرداخت را از لایه API جدا کرده و به یک تابع تطبیق (Reconciliation) واحد منتقل کنید.
اما مدیریت این وضعیتها در مدلهای چندوجهی (Multimodal) که خروجیهای حجیم دارند، پیچیدگیهای بیشتری دارد — به تحلیل ما درباره بهینهسازی استنتاج در مدلهای تصویری مراجعه کنید.




گفتگو