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

۲ اردیبهشت ۱۴۰۵

مهندسی

تسریع گردش‌کارهای عامل‌محور با WebSockets در Responses API

نوشته برایان یو و اشوین نیتان، اعضای کادر فنی

در حال بارگذاری…

هنگامی که از Codex درخواست می‌کنید یک باگ را رفع کند، پایگاه کد شما را برای یافتن فایل‌های مرتبط بررسی می‌کند، آن‌ها را می‌خواند تا زمینه را فراهم کند، ویرایش‌ها را اعمال می‌کند و آزمون‌ها را اجرا می‌کند تا اطمینان حاصل کند که رفع مشکل موفق بوده است. در پشت صحنه، این به معنای ده‌ها درخواست رفت‌وبرگشتی به Responses API است: اقدام بعدی مدل تعیین می‌شود، ابزاری روی رایانه اجرا می‌شود، خروجی ابزار دوباره به API ارسال می‌شود و این روند تکرار می‌شود.

مجموع این درخواست‌ها می‌تواند به چند دقیقه انتظار کاربران برای تکمیل وظایف پیچیده توسط Codex برسد. از نظر تأخیر، حلقه عامل Codex بیشتر زمان خود را در سه مرحله اصلی صرف می‌کند: زمان صرف‌شده در سرویس‌های API (برای اعتبارسنجی و پردازش درخواست‌ها)، استنتاج مدل، و زمانِ سمت کلاینت (اجرای ابزارها و ساخت بافت مدل). استنتاج مرحله‌ای است که در آن مدل روی GPUها اجرا می‌شود تا توکن‌های جدید تولید کند. در گذشته، اجرای استنتاج الگوهای زبانی بزرگ (LLM) روی GPUها کندترین بخشِ حلقه عامل بود؛ بنابراین پنهان‌کردن سربار سرویس‌های API آسان بود. با سریع‌تر شدن استنتاج، سربار انباشته API در یک استقرار عامل‌محور بسیار بیشتر به چشم می‌آید.

در این پست، توضیح می‌دهیم که چگونه حلقه‌های عامل را با استفاده از API به‌صورت سرتاسری ۴۰٪ سریع‌تر کردیم و به کاربران امکان دادیم افزایش سرعت استنتاج را از ۶۵ به نزدیک به ۱٬۰۰۰ توکن در ثانیه تجربه کنند. ما این مسئله را از طریق کش‌کردن، حذف رفت‌وبرگشت‌های غیرضروری شبکه، بهبود پشته ایمنی خود برای شناسایی سریع مشکلات، و—مهم‌تر از همه—ایجاد راهی برای برقراری یک اتصال پایدار به API پاسخ‌ها پیش بردیم، به‌جای اینکه مجبور باشیم مجموعه‌ای از فراخوانی‌های هم‌زمان API انجام دهیم.

نموداری با عنوان «حلقه عامل Codex در عمل» که جریان تکرارشونده‌ای میان Codex و Responses API را نشان می‌دهد؛ در این جریان، فراخوانی‌های ابزار (rg، sed، apply_patch، pytest) و نتایج میان آن‌ها ردوبدل می‌شوند تا به پیام نهایی برسد: «اشکال برطرف شده است.»

زمانی که API به گلوگاه تبدیل شد

در Responses API، الگو پرچمی قبلی مانند GPT‑5 و GPT‑5.2 با سرعتی در حدود ۶۵ توکن در ثانیه (TPS) اجرا می‌شدند. برای عرضه GPT‑5.3‑Codex‑Spark، یک مدل سریع برای کدنویسی، هدف ما دستیابی به سرعتی یک مرتبه بزرگی بیشتر بود: بیش از ۱٬۰۰۰ TPS، که به لطف سخت‌افزار تخصصی Cerebras، بهینه‌سازی‌شده برای استنتاج الگوهای زبانی بزرگ (LLM)، ممکن شده است. برای اینکه مطمئن شویم کاربران می‌توانند سرعت واقعی این مدل جدید را تجربه کنند، ناچار بودیم هزینه اضافی API را کاهش دهیم. 

در حدود نوامبر ۲۰۲۵، ما یک اسپرینت عملکردی را برای Responses API آغاز کردیم و بهینه‌سازی‌های زیادی را برای تأخیر مسیر بحرانی یک درخواست واحد اعمال کردیم: 

  • ذخیره‌سازی توکن‌های رندرشده و پیکربندی مدل در حافظه کش برای رد شدن از توکنیزه سازی پرهزینه و فراخوانی‌های شبکه در پاسخ‌های چندمرحله‌ای
  • کاهش تأخیر پرش‌های شبکه با حذف فراخوانی به سرویس‌های میانی (برای مثال، تعیین وضوح پردازش تصویر) و فراخوانی مستقیم خود سرویس استنتاج
  • بهبود مجموعه ایمنی‌مان تا بتوانیم برخی طبقه‌بندها را برای علامت‌گذاری سریع‌تر مکالمات اجرا کنیم

با این بهبودها، نزدیک به ۴۵٪ بهبود در زمان تا اولین توکن (TTFT) مشاهده کردیم—که نشان می‌دهد API تا چه اندازه پاسخ‌گو به نظر می‌رسد—اما این بهبودها همچنان برای GPT‑5.3‑Codex‑Spark به اندازه کافی سریع نبودند. حتی با وجود این بهبودها، سربار Responses API نسبت به سرعت مدل بیش از حد زیاد بود—یعنی کاربران باید منتظر CPUهایی می‌ماندند که API ما را اجرا می‌کردند، پیش از آنکه بتوانند از GPUهایی که به مدل سرویس می‌دادند استفاده کنند.

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

ایجاد یک اتصال پایدار

برای بهینه‌سازی طراحی، پروتکل انتقال را بازنگری کردیم: آیا می‌توانستیم یک اتصال پایدار را حفظ کنیم و وضعیت را در حافظه ذخیره کنیم، به‌جای اینکه برای هر درخواست پیگیری یک اتصال جدید از طریق HTTP برقرار کنیم و کل تاریخچه گفتگو را ارسال کنیم؟ ایده این بود که فقط اطلاعات جدیدی که نیاز به اعتبارسنجی و پردازش داشت ارسال شود و وضعیت قابل استفاده مجدد برای مدت زمان اتصال در حافظه ذخیره گردد. این کار باعث کاهش سربار ناشی از کار تکراری می‌شود.

ما چند رویکرد متفاوت را در نظر گرفتیم، از جمله WebSockets و استریم دوسویه gRPC. در نهایت WebSockets را انتخاب کردیم، زیرا به‌عنوان یک پروتکل ساده برای انتقال پیام، کاربران مجبور نبودند ساختارهای ورودی و خروجی Responses API خود را تغییر دهند. برای توسعه‌دهندگان مناسب بود و با کمترین اختلال با معماری موجود ما سازگار شد.

نخستین نمونه اولیه WebSocket برداشت ما را از آنچه برای تأخیر API پاسخ‌ها ممکن بود تغییر داد. یک مهندس از تیم Codex با تخصص عمیق در سراسر پشته API، با اجرای یک عامل Codex در طول شب یک نمونه اولیه آماده کرد.

در آن نمونه اولیه، رول‌اوت‌های عامل‌محور به‌صورت یک Response واحدِ بلندمدت مدل‌سازی شده بودند. با استفاده از قابلیت‌های asyncio ، Responses API پس از آنکه یک فراخوانی ابزار در حلقه نمونه‌گیری نمونه‌گیری می‌شد، به‌صورت ناهمگام متوقف می‌شد و Responses API یک رویداد response.done را به کلاینت بازمی‌فرستاد. پس از اجرای فراخوانی ابزار، مشتری‌ها یک رویداد response.append را همراه با نتیجه ابزار بازمی‌فرستادند که حلقه نمونه‌گیری را از حالت مسدود خارج می‌کرد و به مدل اجازه می‌داد ادامه دهد.

در اینجا می‌توان چنین قیاس کرد که فراخوانی ابزار محلی را مانند یک فراخوانی ابزار میزبانی‌شده در نظر بگیریم. وقتی مدل جستجوی وب را فراخوانی می‌کند، حلقه استنتاج متوقف می‌شود، یک سرویس جستجوی وب را فراخوانی می‌کند و پاسخ سرویس را در بافت مدل قرار می‌دهد. در طراحی خود، ما نیز همین کار را انجام دادیم؛ اما به‌جای فراخوانی یک سرویس راه دور، فراخوانی ابزار مدل را از طریق WebSocket به مشتری بازگرداندیم. وقتی کلاینت پاسخ داد، پاسخ فراخوانی ابزار کلاینت را در زمینه قرار دادیم و نمونه‌گیری را ادامه دادیم.

این طراحی بسیار مؤثر بود زیرا کار تکراری مربوط به API را در سراسر استقرار عامل حذف کرد. می‌توانستیم یک‌بار کارهای پیش‌استنتاج را انجام دهیم، برای اجرای ابزارها مکث کنیم، و در پایان یک‌بار کارهای پس‌استنتاج را انجام دهیم.

متأسفانه، این کار به بهای ساختار API کم‌آشناتر و پیچیده‌تر تمام می‌شد. ما می‌خواستیم توسعه‌دهندگان بتوانند بدون نیاز به بازنویسی یکپارچه‌سازی API خود بر اساس یک شیوه تعامل جدید، به‌سادگی پشتیبانی از WebSocket را اضافه کنند.

آشنا نگه داشتن API و در عین حال گام‌به‌گام کردن پشته

برای نسخه‌ای که منتشر کردیم، به یک ساختار آشنا برگشتیم: همچنان از response.create با همان بدنه استفاده کنید، و از previous_response_id برای ادامه دادن زمینه گفتگو از وضعیت پاسخ قبلی استفاده کنید.

در یک اتصال WebSocket، سرور یک کش درون‌حافظه‌ای از وضعیت پاسخ‌های قبلی را در محدوده همان اتصال نگه می‌دارد. وقتی یک response.create بعدی شامل previous_response_id باشد، آن وضعیت را از کش واکشی می‌کنیم، به‌جای اینکه کل مکالمه را از ابتدا بازسازی کنیم.

آن وضعیت ذخیره‌شده در کش شامل موارد زیر است:

  • شیء response قبلی
  • آیتم‌های ورودی و خروجی قبلی
  • تعاریف ابزار و فضاهای نام‌گذاری
  • مصنوعات نمونه‌گیری قابل استفاده مجدد، مانند توکن‌های پردازش‌شده قبلی
نموداری با عنوان «از درخواست‌های متوالی تا اجرای هم‌پوشان» که یک چرخه کار درخواست متوالی را با رویکردی مبتنی بر WebSocket مقایسه می‌کند؛ رویکردی که در آن چندین درخواست در مراحل اعتبارسنجی، پیش‌استنتاج، نمونه‌گیری و پس‌استنتاج با یکدیگر هم‌پوشانی دارند.

با استفاده مجدد از حالتِ پاسخ قبلی در حافظه، توانستیم چندین بهینه‌سازی مهم را پیاده‌سازی کنیم:

  • واداشتن برخی از طبقه‌بندهای ایمنی و اعتبارسنج‌های درخواست ما به پردازش فقط ورودی جدید، نه کل تاریخچه در هر بار
  • نگهداری یک کش درون‌حافظه‌ای از توکن‌های رندرشده که به آن اضافه می‌کنیم تا بتوانیم از توکنیزه‌سازی غیرضروری صرف‌نظر کنیم
  • استفاده مجدد از منطق موفق ما برای حل‌وفصل/هدایت مدل در سراسر درخواست‌ها 
  • هم‌پوشانی کارهای بدون مسدود شدنِ پس از استنتاج، مانند صورتحساب‌گیری، با درخواست‌های بعدی

هدف این بود که تا حد امکان به نمونه اولیه‌ای با حداقل سربار نزدیک شویم، اما با ساختار APIای که توسعه‌دهندگان از پیش آن را درک کرده و بر اساس آن توسعه داده بودند.

تعیین معیار جدیدی برای سرعت

پس از یک تلاش فشرده دوماهه برای ساخت حالت WebSocket، نسخه آلفایی را با استارتاپ‌های کلیدی حوزه عامل‌های کدنویسی راه‌اندازی کردیم تا بتوانند آن را در زیرساخت خود یکپارچه کنند و ترافیک را با اطمینان و به‌تدریج افزایش دهند. کاربران آلفا آن را دوست داشتند و بهبودی تا ۴۰٪(در یک پنجره جدید باز می‌شود) را در جریان‌های کاری عامل‌محور خود گزارش کردند. با توجه به بازخورد مثبت آلفا، آماده عرضه بودیم.

نتایج راه‌اندازی فوری بودند. Codex به‌سرعت بخش عمده‌ای از ترافیک Responses API خود را به حالت WebSocket منتقل کرد و بهبودهای چشمگیری در تأخیر مشاهده کرد. برای GPT‑5.3‑Codex‑Spark، به هدف ۱٬۰۰۰ TPS خود رسیدیم و افزایش‌های لحظه‌ای تا ۴٬۰۰۰ TPS را مشاهده کردیم که نشان می‌دهد Responses API می‌تواند با استنتاج بسیار سریع‌تر در ترافیک واقعی محیط تولید همگام شود. این تأثیر به‌سرعت در جامعه توسعه‌دهندگان نیز نمایان شد:

حالت WebSocket یکی از مهم‌ترین قابلیت‌های جدید در Responses API از زمان راه‌اندازی آن در مارس ۲۰۲۵ بوده است. ما تنها در عرض چند هفته، از ایده تا اجرا در محیط تولید پیش رفتیم؛ آن هم از طریق همکاری نزدیک میان تیم‌های API و Codex در OpenAI. این کار نه‌تنها تأخیر در عرضه عامل‌ها را به‌طور چشمگیری بهبود می‌دهد، بلکه از یک نیاز رو‌به‌رشد برای سازندگان نیز پشتیبانی می‌کند: هرچه استنتاج مدل سریع‌تر می‌شود، سرویس‌ها و سیستم‌های پیرامون استنتاج نیز باید شتاب بگیرند تا این دستاوردها را به کاربران منتقل کنند. 

نویسنده‌ها

Brian Yu،‏ Ashwin Nathan

قدردانی‌ها

تشکر ویژه از تیم‌های Responses API و Codex که روی ایجاد حالت WebSocket کار کردند.