Building the new GitHub Copilot Inline Suggestions Model: Part One

ساخت مدل جدید پیشنهادات درون‌خطی گیت‌هاب کوپایلوت: بخش اول

بررسی کنید که چگونه گیت‌هاب کوپایلوت تکمیل کد، پیشنهاد ویرایش بعدی و پیشنهادات راه دور را در یک مدل واحد ادغام کرد تا تجربه‌ای سریع‌تر و یکپارچه‌تر در کدنویسی فراهم کند.

ساخت مدل جدید پیشنهادات درون‌خطی گیت‌هاب کوپایلوت: بخش اول

۱۶ سپتامبر ۲۰۲۶ توسط جولیا گونگ، بن لیگت و ولوگبک عبدالله‌یف

متن کم‌رنگ (Ghost text) سبک تکمیل خودکار، پیشنهادات ویرایش بعدی در نزدیکی مکان‌نما و ویرایش‌های دورتر، پیش‌تر توسط مدل‌های مجزایی پشتیبانی می‌شدند. ما یک مدل واحد برای هر سه ساختیم و آموختیم که بهترین نتایج زمانی حاصل می‌شوند که آموزش، ارزیابی و طراحی ویرایشگر در کنار هم تکامل پیدا کنند.

در گیت‌هاب کوپایلوت، ماموریت ما پشتیبانی از تمامی جریان‌های کاری توسعه است؛ از کدنویسی به کمک هوش مصنوعی با استفاده از پیشنهادات درون‌خطی گرفته تا مهندسی نرم‌افزار ایجنت‌محور در پنجره VS Code Agents. پیشنهادات درون‌خطی توسط میلیون‌ها توسعه‌دهنده در سراسر جهان استفاده می‌شوند و محبوب هستند، به‌طوری که در ۹۰ روز گذشته بیش از ۶۱ میلیارد درخواست موفق به مدل ارسال شده است، و ما همچنان به ارتقای سطح کیفیت آن و تمامی تجربیات گیت‌هاب کوپایلوت ادامه می‌دهیم.

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

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

ادغام مدل‌ها کیفیت پیشنهاد را بهبود می‌بخشد؛ زیرا به یک مدل واحد اجازه می‌دهد تا به‌جای استفاده از منطق برنامه‌نویسی برای انتخاب میان مدل‌های تخصصی، بهترین ویرایش را برای کار فعلی توسعه‌دهنده به شکل سرتاسری (end-to-end) انتخاب کند. برای مثال، فرض کنید توسعه‌دهنده کلاس Fa را در برنامه تغذیه پنگوئن در زیر تایپ کرده است. با استفاده از مدل‌های مستقل قبلی (چپ)، بالغ‌ترین و آزموده‌شده‌ترین مدل تکمیل همیشه در ابتدا فعال می‌شود. از آنجا که این مدل فقط می‌تواند به پیشوند Fa چیزی اضافه کند، هرآنچه را که بهتر بلد است انجام می‌دهد و FastingPenguin را پیشنهاد می‌کند. با این حال، در این مورد خاص، یک پیشنهاد NES بهتر وجود دارد که توسط مدل ادغام‌شده (راست) پیشنهاد شده است: بازنویسی معنایی Fa به Fish.

قبل (مدل‌های مستقل) بعد (مدل ادغام‌شده)

ما نه تنها می‌توانیم تمام وظایف موجود را در یک مدل حل کنیم، بلکه از آنجا که این مدل می‌تواند چندین ویرایش را در یک پاسخ خروجی دهد، می‌توانیم این ویرایش‌های اضافی را ذخیره (کش) کنیم تا در صورت مطلوب بودن پیشنهادات قبلی، تجربه‌ای سریع‌تر برای ویرایش‌های بعدی ارائه دهیم. این امر یک تجربه روان‌تر و سریع‌ترِ «تب-تب-تب» در سراسر جریان ویرایش ایجاد می‌کند.

برای فعال‌سازی این تجربه ویرایش بهتر، ابتدا وظیفه مدل‌سازی—به‌ویژه قالب پیام و خروجی—را بازطراحی کردیم تا در تمامی وظایف ویرایش کد به شکلی عمومی قابل استفاده باشد. سپس یافته‌های خود را در زمینه کیفیت داده، آموزش و ارزیابی مدل، و طراحی کلاینت از مدل‌های مستقل ترکیب کردیم و علاوه بر انجام آزمایش‌های گسترده اضافی (بیش از ۲۰۰ آزمایش آفلاین و ۲۰ آزمایش آنلاین که در ادامه این پست‌ها به روند آن‌ها خواهیم پرداخت)، مدل و تجربه سرتاسری را به دقت بهینه‌سازی کردیم. نتیجه، یک همراه تایپ باکیفیت‌تر، سریع‌تر و یکپارچه‌تر است که ارزشی فراتر از مجموع اجزای خود دارد.

چرا ادغام؟

پیش از مدل ادغام‌شده، پیاده‌سازی تولیدی پیشنهادات درون‌خطی در VS Code توسط سه مدل مجزا پشتیبانی می‌شد:

  • تکمیل (همان متن کم‌رنگ آشنایی که همه می‌شناسیم و دوست داریم)،
  • NES (پیشنهادات ویرایش بعدی، یا ویرایش‌های نزدیک محدود به چند خط بالا و پایین موقعیت مکان‌نما)،
  • NES راه دور (پیشنهادات سبک NES در بازه‌های طولانی‌تر و دورتر از مکان‌نما).

این کار اختصاصی روی وظایف فردی، با یک مدل تکمیل بسیار بالغ در کنار مدل‌های ویرایش جدیدتر، منجر به تجربه‌ای هماهنگ‌شده توسط کلاینت شد که هرکدام را در جایگاه مناسب خود فعال می‌کرد.

اگرچه این رویکرد برای این وظایف مجزا مؤثر بود و گامی ضروری برای ارائه قابلیت‌های ویرایش جدید به کاربران محسوب می شد، اما کاستی‌های کلیدی را ایجاد کرد؛ نه تنها از نظر کیفیت جریان کاربر (گاهی اوقات به جای یک ویرایش واضح واحد که به بهترین شکل به زمینه خدمت می‌کند، ویرایش‌های زیرپوستی یا قطعه‌قطعه انجام می‌داد) و تاخیر (احتمالاً تا ۴ فراخوانی مدل به ازای هر فرصت، همان‌طور که در شکل زیر نشان داده شده است)، بلکه از نظر قابلیت توسعه برای ویژگی‌های آینده (برای مثال، ویرایش‌های چندفایلی).

شکل ۱. راه‌اندازی اولیه تولیدی برای پیشنهادات درون‌خطی VS Code با سه مدل مستقل: تکمیل، NES و NES راه دور. توجه داشته باشید که این نمودار برای وضوح بیشتر ساده‌سازی شده است و رفتار را از دیدگاه کاربر نشان می‌دهد (رفتار واقعی فراخوانی ارائه‌دهنده پیچیده‌تر است و شامل صدور فراخوانی‌های موازی به تکمیل و NES می‌شود، برای مثال بسته به موقعیت مکان‌نما و سرعت پاسخگویی مدل اولویت‌بندی شده). هر مدل وظیفه مستقل خود را مدیریت می‌کرد، در حالی که کلاینت منطق زمان فعال‌سازی هر مدل را هماهنگ می‌کرد. مدل تکمیل همیشه ابتدا برای پیش‌بینی متن کم‌رنگ در محل مکان‌نمای کاربر فعال می‌شد که در صورت تولید متن کم‌رنگ نمایش داده می‌شد. اگر متن کم‌رنگی تولید نمی‌شد، مدل NES برای بازنویسی پنجره اطراف مکان‌نمای کاربر فراخوانی می‌شد و اگر بازنویسی منجر به یک ویرایش خالص می‌شد، با نوع نمای رندر شده مناسب نمایش داده می‌افتاد. اگر باز هم هیچ ویرایشی پیش‌بینی نمی‌شد، مدل ویرایش راه دور برای پیش‌بینی یک شماره خط بالقوه جهت پرش به آن فعال می‌شد. اگر شماره خطی تولید نمی‌شد، هیچ ویرایشی نمایش داده نمی‌شد. اگر به شماره خط جدیدی پرش می‌کرد، مدل NES یک بار دیگر برای بازنویسی پنجره‌ای در اطراف شماره خط راه دور فراخوانی می‌شد که منجر به یک ویرایش NES راه دور می‌شد.

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

بازطراحی وظیفه ویرایش کد

با دانستن این موضوع که یک مدل ادغام‌شده ستاره قطبی ما است، متوجه شدیم که این فرصتی است تا مسئله ویرایش کد را از نو تعریف کنیم، و کار را از قالب خروجی مدل آغاز کردیم. هیچ‌یک از سه وظیفه مجزای پیش‌بینی پسوند (تکمیل)، بازنویسی پنجره (NES)، یا شماره خط (پرش مکان‌نما)، که به مرور زمان آن‌ها را به صورت سیستماتیک و فردی حل کرده بودیم، به اندازه کافی برای نشان دادن کامل وظایف دیگر گویا نبودند. برای ترکیب این موارد در یک مدل واحد که بتواند هر سه را انجام دهد، به یک قالب خروجی به همان اندازه یکپارچه نیاز داشتیم—این امر به یک راه حل ظریف منجر شد که آن را قالب خروجی «وصله اختلاف» (diff patch) می‌نامیم.

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

برای درک کامل تغییرات قالب خروجی مدل، می‌توان هر یک از سه وظیفه مدل مجزا را به عنوان پسوندهای طبیعی قابلیت‌های پیش‌بینی توکن بعدی LLM در نظر گرفت—تکمیل، کد ناتمام را ادامه می‌داد، NES یک پنجره ویرایش محلی را بازنویسی می‌کرد، و ویرایش‌های راه دور شماره خط بعدی برای پرش را به همراه یک فراخوانی به NES پیش‌بینی می‌کردند. مدل ۳ در ۱ این موارد را کنار هم قرار می‌دهد تا هر سه را به طور همزمان حل کند و در عین حال به مدل اجازه می‌دهد تا چندین ویرایش را در یک پاس پیش‌بینی کند.

برای ملموس‌تر شدن این موضوع، این مثال را در نظر بگیرید که در آن نماد <|cursor|> موقعیت مکان‌نمای کاربر را نشان می‌دهد.

    def feed_penguin(penguin, fo<|cursor|>):
    penguin.meals += 1
    penguin.hungry = False
    penguin.last_ate_time = datetime.now()

    print(f"{penguin.name} ate {fish.type}")

penguin = Penguin("emperor")
feed_penguin(penguin, fish="silverfish")

  

مدل‌های تکمیل، NES و ویرایش راه دور ممکن است موارد زیر را به طور مجزا برای ایجاد بهترین ویرایش برای وظایف مربوطه خود پیش‌بینی کنند:

قالب تکمیل قالب NES (بازنویسی پنجره) قالب ویرایش راه دور
od def feed_penguin(penguin, food): penguin.meals += 1 penguin.hungry = False penguin.last_ate_time = datetime.now() print(f"{penguin.name} ate {food.type}") ۵ (شماره خط بعدی که در آن ماهی باید با غذا جایگزین شود)

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

    /path/to/penguins.py:0
-def feed_penguin(penguin, fish):
+def feed_penguin(penguin, food):
/path/to/penguins.py:5
-    print(f"{penguin.name} ate {fish.type}")
+    print(f"{penguin.name} ate {food.type}")
/path/to/penguins.py:8
-feed_penguin(penguin, fish="silverfish")
+feed_penguin(penguin, food="silverfish")
/path/to/antarctica.py:42
-feed_penguin(penguin2, fish="krill")
+feed_penguin(penguin2, food="krill")

  

پاسخ‌های چند وصله‌ای همچنین از این جهت اهمیت دارند که به ما اجازه می‌دهند وصله‌های بعدی را (در مثال بالا، ویرایش‌های خطوط ۵، ۸ و ۴۲) کش کنیم. پس از پذیرش وصله‌های قبلی، آن‌ها می‌توانند با تاخیر کم ظاهر شوند که منجر به یک تجربه ویرایش کد سریع «تب-تب-تب» می‌شود.

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

علاوه بر بازطراحی وظیفه خروجی، ما همچنین فرصتی را مغتنم شمردیم تا زمینه ارائه شده به مدل به عنوان ورودی را بازنگری کنیم. در ابتدا، ورودی مدل شامل موارد زیر بود:

  • قطعه کدهای اخیراً مشاهده شده بدون شماره خط
  • محتوای فایل فعلی به همراه شماره خطوط
  • تاریخچه ویرایش‌های اخیر کاربر در قالب دیف یکپارچه (unified diff)
  • حوزه اطراف کد برای ویرایش، در مجموع ۱۵+/- خط در اطراف مکان‌نمای (cursor) کاربر برای پد کردن منطقه ویرایش کد
  • کد قابل ویرایش، پنجره بازنویسی که شامل ۲ خط بالا و ۵ خط پایین‌تر از مکان‌نمای کاربر است

فرمت خروجی یکپارچه به ما اجازه داد تا زمینه ورودی را نیز ساده‌سازی کنیم. از آنجا که محدوده وظیفه اکنون امکان ویرایش‌های دلخواه را در هر جای فایل فراهم می‌کرد و به پیش‌بینی پسوند در یک مکان خاص یا بازنویسی یک پنجره ثابت محدود نمی‌شد، توانستیم دو بخش آخر ورودی (یعنی ناحیه اطراف کد برای ویرایش و خود کد برای ویرایش) را حذف کرده و به‌جای آن، یک بلوک ۳ خطی واحد شامل شماره خط و محتوای فایلِ مکانِ فعلیِ مکان‌نما را جایگزین کنیم. یکی از آموخته‌های مهمی که از مدل‌های قبلی با خود به همراه داشتیم این بود که از آنجا که مکان‌نمای کاربر به‌طور مداوم موقعیت خود را تغییر می‌دهد، بخش مربوط به موقعیت مکان‌نما را برخلاف قرارگیری مستقیم در داخل محتوای فایل فعلی، در انتهای پرامپت نگه داشتیم. این کار با به حداکثر رساندن پیشوند مشترک پرامپت با پرامپت‌های قبلی، کش کردن کلید-مقدار (key-value caching) را بهینه‌سازی می‌کند.

ما می‌خواستیم به روشی سیستماتیک به مشکل یکپارچه‌سازی بپردازیم، بنابراین آن را در دو فاز پیش بردیم. ابتدا، قابلیت‌های NES (پیشنهاد ویرایش بعدی) و مسافت طولانی را با استفاده از فرمت پچ دیف یکپارچه در یک مدل واحد ادغام کردیم که از آن به عنوان مدل ۲ در ۱ یاد می‌کنیم. مدل تکمیل کد دست‌نخورده باقی ماند و منطق ارکستراسیون کلاینت به‌گونه‌ای ساده‌سازی شد که ابتدا تکمیل کدها و سپس مدل ۲ در ۱ را در اولویت قرار دهد. سپس مدل ۲ در ۱ را با مدل تکمیل کد ادغام کردیم تا قابلیت‌های متن شبه (ghost text) را نیز اضافه کنیم و به مدل ۳ در ۱ برسیم. در ادامه، روند تجربی خود را شرح داده و برخی از روش‌هایی را که توسعه داده‌ایم، چالش‌هایی که با آن‌ها روبرو شده‌ایم و آموخته‌هایی را که در این مسیر کسب کرده‌ایم، به اشتراک خواهیم گذاشت.

شکل ۲. ما ابتدا مدل‌های NES و NES مسافت طولانی را یکپارچه کردیم تا مدل واحد ۲ در ۱ به دست آید، و سپس با یکپارچه‌سازی مدل ۲ در ۱ با مدل تکمیل کد، مدل ۳ در ۱ را ایجاد کردیم.

فاز ۱: مدل ۲ در ۱

ما عمداً تصمیم گرفتیم که ابتدا به ویرایش‌های NES و مسافت طولانی بپردازیم و سپس سراغ تکمیل کدها برویم. طبق تجربه ما، مدل تکمیل کد بیشترین حجم ترافیک پیشنهادات درون‌خطی را به خود اختصاص می‌داد (بیش از ۷۰٪ فرصت‌های ویرایش). آزمایش‌های قبلی تیم نیز نشان داده بود که پیشنهادات NES بیشتر از متن شبه (ghost text) در معرض رد شدن توسط کاربران هستند. از این رو، تصمیم گرفتیم ابتدا دو مدل دیگر را یکپارچه کنیم و مدل اصلی تکمیل کد را در آخرین مرحله انجام دهیم. از آنجا که از فرمت خروجی پچ دیف می‌توان برای مدل ۲ در ۱ استفاده کرد، این بدان معنا بود که آموزش‌های بعدی برای مدل ۳ در ۱ نیز می‌توانستند مستقیماً از داده‌ها و سایر آموخته‌های مدل ۲ در ۱ بهره‌مند شوند و سرعت آزمایش‌ها را افزایش دهند.

ارزیابی

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

  • ارزیابی آفلاین: تست‌های آفلاین هدفمند به ما کمک کردند تا رفتار تکمیل کد، سناریوهای دشوار ویرایش، اعتبار پچ، طول پیشنهاد و تعادل میان انواع مختلف ویرایش‌ها را بررسی کنیم.
  • آزمایش داخلی (Dogfooding): استفاده روزمره مشکلاتی را آشکار کرد که امتیازات تجمیعی ممکن است از دست بدهند؛ مانند پیشنهاداتی که معقول بودند اما زمان‌بندی ضعیفی داشتند، بیش از حد مشتاقانه بودند، ناقص بودند یا به شکل نامناسبی ارائه شده بودند.
  • ارزیابی انسانی در مقیاس وسیع: همچنین ارزیابی انسانی را با برنامه‌نویسانی از پیشینه‌های مختلف روی هزاران وظیفه کدنویسی و با استفاده از قالب‌های بازخورد ساختاریافته انجام دادیم تا ارزیابی کمی از کیفیت مدل از دید توسعه‌دهندگان واقعی به دست آوریم.
  • آزمایش‌های آنلاین کنترل‌شده: نامزدهای امیدوارکننده مدل با استفاده از تست A/B به صورت زنده پیاده‌سازی شدند تا بتوانیم تأثیر نهایی مدل را روی کاربران واقعی ارزیابی کنیم.

هر مرحله به یک سوال متفاوت پاسخ داد. ارزیابی آفلاین به ما گفت که آیا یک نامزد ارزش آزمایش کردن را دارد یا خیر. آزمایش داخلی (Dogfooding) دیدگاهی بی‌واسطه از نحوه حس شدن مدل در جریان‌های کاری واقع‌گرایانه به ما داد. ارزیابی انسانی، تجربه آزمایش داخلی را کمّی کرد و بازخوردهای ساختاریافته‌ای ارائه داد. آزمایش‌های آنلاین به ما نشان دادند که آیا سیستم کامل در مقایسه با نسخه تولیدی موجود، ارزشی در استفاده ایجاد می‌کند یا خیر.

بنچ‌مارک‌های آفلاین

ارزیابی آفلاین برای مدل‌های اصلی NES عمدتاً شامل ۳ بنچ‌مارک بود که هر کدام ابعاد مختلفی از مدل‌های NES را ارزیابی می‌کردند:

  • تست‌های شبیه‌سازی (STests). مجموعه‌ای از ضبط‌های فضای کاری که به‌دقت توسط تیم ما گردآوری شده‌اند و رفتارهای پیشنهادیِ ضروری (must-have)، مطلوب (nice-to-have) و غیرقابل‌قبول (must-avoid) را نشان می‌دهند.
  • HumanEval. نسخه توسعه‌یافته‌ای از مجموعه داده HumanEval شرکت OpenAI که برای ارزیابی قابلیت‌های تکمیل کد مدل‌ها در بازه‌های تک‌خطی، چندخطی و تصادفی از کد گسترش یافته است.
  • نوع نمای خروجی (Output View Kind). نمونه‌هایی که از مدل‌های تولیدی قبلی عبور داده شده‌اند، شامل انواعی از نماهای خروجی (متن شبه، NES پهلو‌به‌پلو، بدون ویرایش و غیره) مدل‌ها که برای ردیابی هرگونه اختلاف در توزیع انواع نمای تولید شده توسط نامزدهای جدید در مقایسه با مدل‌های اصلی استفاده می‌شوند.

آموزش مدل‌های نسخه ۱ (v1): دیف-پچ NES

ما با ساخت ابزارهایی برای تبدیل دقیق ارزیابی مدل اصلی و داده‌های آموزشی به فرمت پچ دیف شروع کردیم. پس از تبدیل داده‌ها، برخی مدل‌های اولیه تنظیم دقیق نظارت‌شده (SFT) را آموزش دادیم و سپس یادگیری تقویتی (RL) را روی داده‌های آموزشی اختصاصی NES اعمال کردیم؛ بیایید این دور از مدل‌ها را مدل‌های نسخه ۱ بنامیم.

همان‌طور که انتظار داشتیم، مدل‌های حاصل توانستند در بنچ‌مارک‌های آفلاین هم‌سطح با مدل‌های NES عمل کنند، اما توزیع ویرایش‌هایی که یاد گرفته بودند دقیقاً همان ویرایش‌های NES بود، بدون تقریباً هیچ‌گونه ویرایش در مسافت طولانی. همچنین متوجه شدیم که تفاوت در فرمت داده‌ها، با وجود استفاده از نمونه‌های داده یکسان، گاهی اوقات منجر به رفتارهای متفاوتی می‌شود. برای مثال، ویرایش‌ها تمایل داشتند تدریجی‌تر باشند، که احتمالاً به این دلیل است که فرمت پچ دیف به طور طبیعی تغییرات هدفمند را در مکان‌های خاص بیان می‌کند تا بازنویسی کل یک پنجره هدف (این موضوع بعداً هنگام بحث درباره مدل ۳ در ۱ دوباره مطرح خواهد شد). پس از پیاده‌سازی این مدل‌ها، چندین نکته ظریف سمت کلاینت را برای مدیریت این فرمت خروجی جدید، از جمله کش کردن و مدیریت خطا، کشف کردیم که در همان ابتدا اصلاح شدند تا نامزدهای مدل‌های آینده بتوانند از این آزمایش آزمایشی بهره‌مند شوند.

اکنون زمان آن رسیده بود که ویرایش‌های مسافت طولانی را نیز اضافه کنیم. در فرمول‌بندی پچ دیف برای این مشکل، یک ویژگی ظریف برای حالت NES مسافت طولانی پدیدار می‌شود: یک ویرایش NES + یک ویرایش NES مسافت طولانی = یک پاسخ چند پچی (multi-patch response). پاسخ‌های چند پچی همچنین به دلیل اقتصاد مقیاس در تجربه کاربری که با کش کردن پچ‌های بعدی برای ویرایش‌های سریع بعدی به دست می‌آید، بسیار حیاتی بودند.

اما چگونه قرار بود پاسخ‌های چند پچی به دست آوریم وقتی مدل‌های موجود ما قابلیت چند پچی نداشتند؟

آموزش مدل‌های نسخه ۲ (v2): افزودن NES مسافت طولانی

گنجاندن قابلیت‌های ویرایش مسافت طولانی کار ساده‌ای نبود، زیرا مجموعه‌های داده موجود برای ویرایش‌های مسافت طولانی و NES قابل ادغام نبودند؛ چرا که آن‌ها از فایل‌ها و تاریخچه‌های ویرایش کاملاً متفاوتی آمده بودند. ما نیاز داشتیم تا داده‌های چند ویرایشی را از داده‌های تک ویرایشی بوت‌استرپ (Bootstrap) کنیم. در نهایت به دو رویکرد برای حل این مشکل رسیدیم:

  • بوت‌استرپ کردن پاسخ‌ها با استفاده از برچسب‌های شبه (pseudolabels) مدل نسخه ۱. ما ابتدا با انتقال برچسب خروجی به داخل تاریخچه ویرایش، داده‌های NES با فرمت پچ دیف را به جلو هدایت کردیم (fast-forward). سپس این داده‌های جلوبرده‌شده را به بهترین مدل‌های نسخه ۱ که آموزش داده بودیم، خوراندیم. اگر مدل یک پاسخ ویرایشی تولید می‌کرد، این برچسب شبه را به عنوان یک پچ دیگر به برچسب خروجی داده اصلی اضافه می‌کردیم. این روند را تا زمانی که مدل نسخه ۱ هیچ ویرایشی در پاسخ خود تولید نکند، تکرار می‌کردیم. این کار نمونه‌هایی با n پچ تولید کرد که سپس می‌توانستیم به مدل یاد دهیم تا آن‌ها را تولید کند. برای جزئیات، به شکل زیر مراجعه کنید.
  • پخش مجدد ویرایش (Edit playback). ما ورودی مدل داده‌های NES را که شامل تاریخچه‌های ویرایش کاربر بود، تجزیه (parse) کردیم و از آن ویرایش‌ها برای «به عقب برگرداندن» (rewind) وضعیت فایل فضای کاری به اندازه ۱ تا n ویرایش استفاده کردیم. سپس آن ویرایش‌ها را از تاریخچه ویرایش در ورودی حذف کردیم و به ابتدای خروجی اضافه کردیم (prepend).

شکل ۳. دو فرآیندی که طی آن‌ها به‌طور مکرر داده‌های چند پچی را با جلوبردن داده‌های تک پچی بوت‌استرپ کردیم. بالا: ابتدا داده‌ها را با داشتن n پچ در خروجی با انتقال برچسب خروجی به تاریخچه ویرایش جلو بردیم. اگر مدل پاسخی برای این نمونه جلوبرده‌شده تولید می‌کرد، آن خروجی به برچسب خروجی نمونه اصلی اضافه می‌شد تا پاسخی با n+1 پچ ایجاد شود. پایین: ما با انتقال ویرایش‌های اخیر از تاریخچه ویرایش برای بازگردانی وضعیت جلسه و پیش‌بینی آن‌ها به عنوان بخشی از خروجی مدل، پخش مجدد ویرایش را انجام دادیم.

پس از اینکه این رویکردها داده‌های کافی را به دست آوردند، داده‌ها را با استفاده از یک ارزیاب مبتنی بر مدل زبانی بزرگ (LLM-as-a-judge) و فیلترهای کیفیت مبتنی بر قانون فیلتر کردیم. معیارهای موجود در این فیلتر از درک ما از ترجیحات توسعه‌دهندگان در طول توسعه مدل‌های ویرایش کد قبلی‌مان، مانند صحت، ضرورت، و همسویی با قصد کاربر و تاریخچه ویرایش، استخراج شده بودند و فیلترهای مبتنی بر قانون نیز اعتبار پچ و رفتارهای نامطلوب مانند تکرار کد را پوشزش می‌دادند.

سپس از SFT برای آموزش یک مدل جدید روی ترکیب جدیدی از این داده‌ها و داده‌های قبلی NES استفاده کردیم، که مدل‌هایی را به دست آورد که به طور متوسط ۱.۳ تا ۱.۴ پچ تولید می‌کردند، در مقایسه با میانگین‌های نزدیک به ۱ قبلی. اگرچه این عدد ممکن است کوچک به نظر برسد، اما بعدها متوجه شدیم که این یک نقطه ایده‌آل (sweet spot) است که تفاوت چشمگیری در تجربه کاربری ایجاد می‌کند بدون اینکه کیفیت را افت دهد. از این گذشته، برای دستیابی به میانگین ۲ پچ در هر پاسخ، ویرایش‌های مسافت طولانی باید به‌طور متوسط در هر فرصت ویرایشی اتفاق می‌افتادند، که در تنظیمات تولیدی موجود با یک مدل ویرایش مسافت طولانی اختصاصی این‌طور نبود.

یادگیری تقویتی (RL) به روشی مشابه روی مدل‌های SFT نسخه ۲ اعمال شد و از همان تکنیک‌ها برای بوت‌استرپ کردن داده‌های چند پچی استفاده گردید. منطق ارزیاب نیز برای گنجاندن راهنمایی‌هایی جهت پاسخ‌های چند پچی تغییر یافت. ما متوجه شدیم که این مدل در مقایسه با مدل SFT نتایج آفلاین بسیار قوی‌تری دارد.

ترتیب پچ‌ها (Patch ordering)

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

در طول فیلتر کردن داده‌ها، علاوه بر معیارهای کیفیت ویرایش، معیار جدیدی را با هدف ترتیب‌بندی ویرایش‌ها اضافه کردیم که نیازمند جریان منطقی روانی از ویرایش‌ها در سراسر فایل بر اساس اولویت و نزدیکی به مکان‌نمای کاربر بود. این انتخاب بصری به نظر می‌رسد زیرا از تجربه ما در ارکستراسیون سه مدل مستقل در کلاینت استخراج شده بود. منطق این بود که ابتدا مدل تکمیل کد، سپس مدل NES، سپس مدل پرش مکان‌نما و در صورت تمایل مدل پرش مکان‌نما برای پرش، یک فراخوانی بعدی برای مدل NES را استعلام کنیم.

علاوه بر گنجاندن این معیارها در پایپ‌لاین فیلتر کردن داده‌ها، آن‌ها را با برخی نکات ظریف اضافی در تابع پاداش (reward function) در ارزیاب یادگیری تقویتی نیز گنجاندیم، که متوجه شدیم این امر برای اولویت‌بندی ویرایش‌های منطقاً منسجم مهم است.

بنچ‌مارک ارزیابی شبه‌آنلاین (The Pseudo-Online Evaluation)

مدل‌های نسخه ۲ در تست A/B آنلاین در مقایسه با کنترل سه مدلی در تولید، یک بهبود تابعی-پله‌ای (step-function improvement) بودند. به جز نرخ‌های رد شدن که کمی افزایش یافته بود، سایر معیارها مانند نرخ پذیرش، نرخ نمایش، کاراکترهای پذیرفته‌شده و حفظ‌شده، و معیارهای تعامل کاربر هیچ تغییر معنادار آماری نداشتند. اما یک یافته عجیب پدیدار شد: نتایج آفلاین پیش‌بینی‌کننده خوبی برای نتایج آنلاین نبودند. برای مثال، یکی از قوی‌ترین نامزدهای آفلاین با بالاترین امتیاز ثبت‌شده در STests، در پرواز A/B به خوبیِ مدل دیگری با امتیاز STests پایین‌تر عمل نکرد. این موضوع ما را به فکر واداشت: چگونه فاصله بین نتایج آفلاین و آنلاین خود را پر کنیم تا بتوانیم با کارایی و اطمینان بیشتری روی نامزدهای مدل تکرار و آن‌ها را بهبود دهیم؟

نتیجه، چهارمین بنچمارک آفلاین بود که آن را ارزیابی شبه‌آنلاین (POE) می‌نامیم. این بنچمارک شامل سه سطل نمونه با اندازه‌های برابر است که نماینده موارد پذیرش، رد و بدون ویرایش از خروجی‌های مدل تولیدی، به همراه سیگنال‌های کیفیت پیشنهاد هستند. این بنچمارک هر یک از نمونه‌های موجود در این گروه‌ها را روی مدل نامزد اجرا می‌کند. سپس از یک مدل زبان بزرگ (LLM) به عنوان داور با استفاده از یادگیری درون‌زمینه‌ای (in-context learning) استفاده می‌کند تا با توجه به نتیجه شناخته‌شده آن نمونه، مشخص کند که آیا پاسخ نامزد پذیرفته می‌شود یا رد. حیاتی‌ترین معیارهای به‌دست‌آمده از این بنچمارک عبارتند از: نرخ رد (مقدار کمتر بهتر است)، نسبت نرخ پذیرش به رد (مقدار بیشتر بهتر است) و نسبت مواردی که رد شده بودند و به حالت بدون ویرایش درآمدند به مواردی که پذیرفته شده بودند و بدون ویرایش درآمدند (معیاری برای سنجش اینکه آیا مدل یاد گرفته است که برای پاسخ‌های ضعیف قبلی، بدون از دست دادن پاسخ‌های خوب قبلی، بیشتر امتناع کند). ما همچنین مواردی مانند خطاهای اعتبار وصله (patch) را که بعداً درباره‌شان صحبت خواهیم کرد، کمّی‌سازی کردیم، و همین‌طور معیارهای دیگری مانند تعداد وصله‌ها در پاسخ‌ها را سنجیدیم. این معیارها، اگرچه اذعان می‌کنیم که هنوز یک پروکسی بی‌نقص نیستند، اما به پیش‌بینی‌کننده‌تری قوی‌تر برای عملکرد آنلاین نامزدهای مدل بعدی تبدیل شدند.

کالیبراسیون درجه‌بندی‌کننده و فیلتر کیفیت

توسعه بنچمارک POE سوال دیگری را نیز مطرح کرد: آیا با درجه‌بندی‌کنندگان جدید یادگیری تقویتی (RL) مختص وصله تفاوت (diff patch)، چیزی را از دست داده بودیم؟ آیا می‌توانستیم با استفاده از داده‌ها و سیگنال‌های کیفیت اضافی آن را بهبود ببخشیم؟ تصمیم گرفتیم با اتخاذ رویکرد خودپالایشی (self-refinement) برای درجه‌بندی‌کننده، آن را بهتر با بازخوردهای کاربر کالیبره کنیم. ما یک مهارت ایجنت توسعه دادیم که چرخه زیر را انجام می‌داد:

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

شایان ذکر است که سیگنال‌های کیفیتی که ما استفاده می‌کنیم اغلب پر سر و صدا (noisy) هستند. با این حال، از طریق این فرآیند، توانستیم به‌طور خودکار میزان تطابق بین درجه‌بندی‌کننده RL و این سیگنال‌های کیفیت را افزایش دهیم، به طوری که منفی‌های کاذب ۸.۴ درصد و مثبت‌های کاذب ۱.۱ درصد کاهش یافتند. این امر قابلیت اطمینان پایپ‌لاین‌های آموزش، ارزیابی و فیلتر داده‌های ما را بهبود بخشید.

مقابله با وصله‌های نامعتبر

نسخه‌های بعدی مدل v2 از درجه‌بندی‌کننده به‌روزرسانی‌شده RL و انتخاب مدل آفلاین بهتر بهره‌مند شدند، اما با استفاده منظم‌تر از مدل‌ها، چند چالش دیگر نیز پدیدار شد. یکی از این چالش‌ها وصله‌های نامعتبر بود.

متوجه شدیم که مدل گاهی اوقات وصله‌هایی ایجاد می‌کند که بدفرم هستند یا اشتباهاتی را معرفی می‌کنند که از نظر برنامه‌نویسی قابل تأیید هستند. ما اعتبار وصله را با دو سطح مشخص کردیم: مسائل بحرانی که بر تجربه کاربری تأثیر می‌گذارند و وصله‌های زیربهینه (suboptimal) که از نظر توکن ناکارآمد هستند. ما فیلترهای مبتنی بر قوانین دقیق را برای گرفتن این مسائل توسعه دادیم. نمونه‌ای از یک مسئله بحرانی، تکرار کد در خطوط قبل یا بعد از خطی است که مکان‌نمای (cursor) کاربر در آن قرار دارد. این موارد برای کاربر قابل مشاهده است و تأثیر منفی بر پیشنهادهای دریافتی آن‌ها دارد. در مقابل، نمونه‌ای از یک مسئله زیربهینه، وصله‌های بدون عمل (no-op) هستند، یا وصله‌هایی که در آن‌ها اضافات و حذف‌ها کاملاً با هم مطابقت دارند؛ به عبارت دیگر، وصلاهایی که کار خود را به طور کامل خنثی می‌کنند. این موارد از منظر توکن و تاخیر (latency) هدر دادن منابع بودند، اما برای ویرایش‌های ارائه شده به کاربر مضر نبودند.

گنجاندن این بررسی‌های اعتبار وصله مبتنی بر قوانین در درجه‌بندی‌کننده RL، نسبت پاسخ‌های نامعتبر را کاهش داد، اما بهبود آن کمتر از حد انتظار بود. ما به بررسی توزیع داده‌ها در مجموعه داده RL خود روی آوردیم و این فرض را مطرح کردیم که این مجموعه داده، زمینه‌های رایج در محیط تولید را که منجر به این خطاهای مدل می‌شوند، ثبت نکرده است. برای اعتبارسنجی این ظن، داده‌کاوی هدفمندی را روی نمونه‌هایی از آخرین مدل‌های v2 که باعث خرابی اعتبار وصله شده بودند، انجام دادیم. ما پاسخ‌ها را از طریق بررسی‌های اعتبار وصله خود اجرا کردیم، آن‌ها را به‌طور خودکار با استفاده از یک حلقه بازخورد تکرارمحور LLM که توسط آن بررسی‌ها هدایت می‌شد ترمیم کردیم، و نمونه‌های ترمیم‌شده را از نظر کیفیت فیلتر نمودیم. با آزمایش دقیق نسبت نمونه‌برداری از این نمونه‌ها در طول آموزش، توانستیم مشکلات اعتبار وصله را بدون تأثیر منفی بر سایر سناریوها کاهش دهیم.

آخرین موردی که برای بررسی‌های اعتبار وصله ارزیابی کردیم این بود که آیا هم مسائل بحرانی و هم مسائل زیربهینه را جریمه کنیم، یا فقط مسائل بحرانی را. ما این فرضیه را مطرح کردیم که اجازه دادن به مدل برای تولید وصله‌های زیربهینه ممکن است سیگنال پاداش را برای کاهش مسائل بحرانی پاکسازی کند و همچنین مزایای غیرمنتظره‌ای داشته باشد (به عنوان مثال، یک وصله بدون عمل بین دو وصله معتبر ممکن است تقریباً به عنوان یک «زنجیره تفکر» عمل کند زیرا مدل مکان‌های احتمالی ویرایش را بررسی می‌کند). در واقع، هنگامی که جریمه وصله‌های زیربهینه را حذف کردیم، کاهش مداوم‌تری را در مسائل بحرانی بدون آسیب قابل توجه به سایر معیارهای کیفیت آفلاین مشاهده کردیم.

آموزش مدل‌های v3: مقابله با مشکلات در انتشار الگوی درج

همان‌طور که به یک نامزد قابل انتشار قوی نزدیک‌تر می‌شدیم، یک مسئله مداوم دیگر از طریق استفاده داخلی (dogfooding) خود را نشان داد: درج‌های جابجا شده در طول انتشار الگو، و به‌طور خاص، محتوای صحیح با مکان‌یابی نادرست. این یک حالت خرابی بسیار خاص در مدل‌های v2 بود که در آن پاسخ مناسب این بود که محتوای جدید را در مکان دیگری در فایل درج کنند تا الگوی درج‌های تکراری را پیرو هدف کاربر منتشر کنند. برای مثال، اگر کاربر فیلد password را پس از email در یک فرم وب اضافه کرده باشد، انتظار این است که برچسب Password مربوطه را پس از برچسب Email، و بررسی validate_password را پس از بررسی validate_email اضافه کند. با این حال، مشخص شد که مدل‌های v2 اغلب Password را قبل از Email، یا حتی در انتهای لیست فیلدها قرار می‌دهند.

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

ابتدا، ما از داوران LLM استفاده کردیم تا نمونه‌هایی را از خروجی‌های مدل v2 که این حالت خرابی خاص را نشان می‌دادند شناسایی کنیم: تولید محتوای صحیح اما قرار دادن نادرست آن زمانی که هدف کاربر انتشار الگو را نشان می‌داد. این کار به طرز شگفت‌آوری دشوار ثابت شد زیرا سناریوهای درج فاصله دور در مقایسه با سناریوهای جایگزینی نسبتاً نادر بودند. حتی پس از ترمیم پاسخ مبتنی بر LLM و فیلتر کردن کیفیت، هزاران نمونه مثبت به دست آوردیم، اما تنها چند صد نمونه منفی که خطای مکان‌یابی اصلی را نشان می‌دادند، به دست آمد.

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

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

پس از انجام این کار، خروجی‌های مدل را در طول آموزش RL بررسی کردیم و تعجب کردیم که دیدیم درجه‌بندی‌کننده RL با وجود نمایش کافی داده‌ها، خطاهای انتشار الگوی درج از این نوع را به‌طور قابل اعتمادی علامت‌گذاری نمی‌کند. با توجه به اهداف متعدد در درجه‌بندی‌کننده از قبل پیچیده، برای جلوگیری از بی‌ثبات کردن رفتار موجود درجه‌بندی‌کننده، تکنیک جدیدی را برای درجه‌بندی‌کننده RL توسعه دادیم که یک درجه‌بندی‌کننده کمکی جدید را معرفی کرد تا به‌طور خاص به نقص پیروی از الگوی درج در درجه‌بندی‌کننده اصلی رسیدگی کند. ما این درجه‌بندی‌کننده را به نمونه‌هایی محدود کردیم که دارای درج در حقیقت زمینی (ground truth) یا پیش‌بینی بودند.

در مجموع، نمونه‌های هدفمند، وزن‌دهی بیشتر به نمونه‌های سخت، و تغییرات درجه‌بندی‌کننده در نهایت رفتار درج مشکل‌ساز مدل‌ها را اصلاح کرد و منجر به مدل‌های v3 شد. این ترکیب نمایش داده‌ها، تعادل‌دهی داده‌ها، و افزایش درجه‌بندی‌کننده یک یادگیری بسیار مهم برای ما بود، و تکنیکی بود که هنگام توسعه مدل ۳ در ۱ دوباره از آن استفاده کردیم.

بنچمارک ملزومات کیفی (Qualitative Must-Haves)

چالش‌هایی که با مسئله انتشار الگوی درج داشتیم، که تنها از طریق استفاده داخلی و نه از طریق آزمایش پرواز (flighting) و نه از طریق ارزیابی آفلاین آشکار شد، ما را بر آن داشت تا درباره شکاف‌های بالقوه در روش‌شناسی ارزیابی موجود خود فکر کنیم. این امر باعث شد تا پنجمین بنچمارک ارزیابی آفلاین، یعنی ملزومات کیفی (QMH) را توسعه دهیم که مجموعه زنده‌ای از سناریوهای انتخاب‌شده است که مدل‌های قبلی و فعلی آن‌ها را چالش‌برانگیز یافته‌اند که اغلب از طریق استفاده داخلی کشف شده‌اند.

هدف این بنچمارک با سایرین متفاوت است. در حالی که بقیه مقیاس بزرگ‌تر و کمّی هستند، ما عمداً این مورد را سبک و کوچک نگه داشتیم تا بتواند به عنوان یک مجموعه «قناری» برای علامت‌گذاری مشکلات مدل‌ها و فیلتر کردن سریع یا تأیید نامزدهای مدل عمل کند. علاوه بر این، به جای اندازه‌گیری کلی کیفیت مدل، بیشتر بر جلوگیری از رفتارهای بد گذشته تمرکز دارد. با این حال، این نام تا حدی نامناسب است، زیرا همان‌طور که داوران LLM را توسعه دادیم که بتوانند به عنوان پروکسی‌های انسانی برای قضاوت در مورد نتایج عمل کنند، در نهایت کمّی شد.

تلاش‌های مدل‌سازی ۲ در ۱ و ۳ در ۱ ما هر دو از این بنچمارک به عنوان یک روش اکتشافی برای رگرسیون‌های مدل و ناامیدی بالقوه کاربر، و همچنین تکرار سریعی که به دلیل اندازه کوچک خود فراهم کرده است، بهره زیادی برده‌اند. ما همچنین سناریوهایی را که به مجموعه داده QMH اضافه کرده بودیم، در ارزیابی‌های انسانی در مقیاس بزرگ‌تر که انجام می‌دهیم، گنجاندیم.

یکی از بزرگ‌ترین آموخته‌های ما در توسعه این بنچمارک این بوده است که از آنجایی که مدل‌هایی که آموزش می‌دهیم دارای پایپ‌لاین‌های داده و آموزش مشترکی هستند، اگر به دقت تحت نظارت نباشند، رفتارهای مشکل‌ساز می‌توانند دوباره در مدل‌های آینده سر برآورند، به خصوص اگر مدل‌های آینده اهداف متفاوتی مانند افزودن یک ویژگی جدید داشته باشند. داشتن مجموعه کوچکی از نمونه‌ها که رفتارهای بحرانی را اندازه‌گیری می‌کنند و می‌توانند به سرعت رگرسیون‌ها را علامت‌گذاری کنند، یا اگر به طور پیشگیرانه استفاده شوند، از رگرسیون‌های احتمالی جلوگیری کنند، برای انتخاب مدل یک تغییر دهنده بازی (game-changer) بوده است. شایان ذکر است که برای قابل اعتماد بودن این مجموعه، باید پوشش خوبی داشته باشد، بنابراین صرف وقت برای انتخاب نمونه‌ها و اطمینان از اینکه آن‌ها نمایشی عادلانه از رگرسیون‌های گذشته و بالقوه هستند، مهم بوده است.

یکی دیگر از آموخته‌هایی که باید بر آن تأکید کرد، اهمیت استفاده داخلی (dogfooding) است. هیچ چیز جای تلاش خودتان را نمی‌گیرد، به خصوص که همه چیز را نمی‌توان به طور کامل در یک کارت امتیاز کمّی ثبت کرد. آزمایش مرزهای مدل توسط خود ما به عنوان کاربران نهایی، چشم‌انداز بی‌نظیری را فراهم می‌کند. به عنوان توسعه‌دهندگان محصولات برای توسعه‌دهندگان، ما این را ضروری می‌دانیم و بخش مهمی از فرهنگ خود می‌دانیم که به ارائه مدل‌های ویرایش کد با کیفیت بالا افتخار می‌کنیم.

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

دستاوردها و آموخته‌ها

یکپارچه‌سازی NES و ویرایش‌های فاصله دور بر بازنویسی خروجی به صورت یک فرمت وصله تفاوت واحد استوار بود. این یک نمایش می‌توانست هر کار قبلی را بیان کند، و نکته مهم اینکه، چندین ویرایش را در یک پاسخ چند وصله‌ای زنجیر کند، که این پایه و اساس یک جریان tab-tab-tab با تأخیر کم بود. چند درس اصلی برجسته بود:

  • داده‌های بوت‌استراپ هنگام پیشگام شدن در سناریوهای جدید. داده‌های آموزشی چند وصله‌ای وجود نداشتند، بنابراین ما آن‌ها را از داده‌های ویرایش تکی با استفاده از برچسب‌های شبه (pseudolabels) و پخش مجدد ویرایش، به دنبال آن فیلتر کردن کیفیت، سنتز کردیم. این کار پایه‌گذار داده‌های SFT و RL برای شروع چرخه توسعه مدل چند وصله‌ای شد.
  • یک دستورالعمل قابل تکرار برای رفع حالت‌های خرابی خاص پدیدار شد. نمونه‌های هدفمند، وزن‌دهی بیشتر به نمونه‌های سخت، و یک درجه‌بندی‌کننده کمکی دروازه‌دار برای اصلاح مدل از یک مدل خوب به یک نامزد قابل انتشار حیاتی بودند. این کار مشکلات وصله‌های نامعتبر و انتشار الگوی درج را برطرف کرد و به تکنیک اصلی ما برای آینده تبدیل شد.
  • تلاش برای پر کردن شکاف بین آفلاین و آنلاین. این امر باعث ایجاد بنچمارک ارزیابی شبه‌آنلاین (POE) و یک چرخه خودپالایشی برای کالیبره کردن درجه‌بندی‌کننده در برابر خروجی‌های مدل و سیگنال‌های کیفیت شد، و تنگ کردن شکاف آفلاین به آنلاین منجر به آزمایش‌های سریع‌تر و مطمئن‌تر شد.
  • آوردن درس‌ها و شهودهای آزموده شده به قلمروی جدید. هم در فیلتر داده و هم در درجه‌بندی‌کننده RL، این دستورالعمل را اعمال کردیم که ویرایش‌ها باید بر اساس اولویت و مجاورت با مکان‌نما به طور منطقی به سمت بیرون جریان پیدا کنند. این از تجربه ما در هماهنگ کردن یک تجربه موفق برای پیشنهادهای درون‌خطی در راه‌اندازی سه مدلی ناشی شده است.
  • استفاده داخلی (dogfooding) می‌تواند مواردی را که معیارها گم کرده‌اند صید کند. مسائلی که در حالت آفلاین و آنلاین نامرئی بودند تنها از طریق استفاده روزمره ظاهر شدند، و این امر انگیزه ایجاد بنچمارک کوچک «قناری» ملزومات کیفی را برای محافظت در برابر رگرسیون‌ها در پایپ‌لاین‌های مشترک فراهم کرد.

نتایج آنلاین

ما نامزدهای مدل ۲ در ۱ را در برابر خط مبنای تولید سه مدل مستقل (تکمیل‌ها، NES و پرش مکان‌نما) آزمایش پرواز (flighting) کردیم. مشاهده شد که نامزد نهایی قابل انتشار هیچ رگرسیون از نظر آماری معناداری در هیچ یک از معیارهای کلیدی (نرخ پذیرش، نرخ رد، نرخ نمایش، تعامل مکرر، و کاراکترهای پذیرفته شده و حفظ شده) ندارد. علاوه بر این، ما کاهش ۷ درصدی در زمان لازم برای نمایش پیشنهادها به کاربر را مشاهده کردیم، همراه با کاهش ۵۹ درصدی در توکن‌های خروجی. این موارد بیشتر به دلیل فرمت‌های خروجی و ورودی کارآمدتر و منطق سمت کلاینت ساده‌تر بود.

آن را امتحان کنید

تجربه یکپارچه پیشنهادهای درون‌خطی اکنون برای کاربران پولی GitHub Copilot در VS Code در دسترس است. به آخرین نسخه VS Code به‌روزرسانی کنید، سپس مطمئن شوید که پیشنهادهای ویرایش بعدی فعال هستند.

دفعه بعد که در ویرایشگر هستید، چه در حال کار روی بازسازی کد (refactor) باشید و چه در حال نوشتن یک تابع جدید، آن را امتحان کنید. امیدواریم از تجربهٔ «تب-تب-تب» لذت ببرید و مشتاقانه منتظر شنیدن نظرات شما هستیم!

در بخش دوم این پست وبلاگ، عمیق‌تر به نحوهٔ انتقال به مدل ۳ در ۱ برای پیشنهادات درون‌خطی خواهیم پرداخت. منتظر باشید!

سپاسگزاری‌ها

تشکر ویژه از Luciana Abud، Alexandru Dima، Yu Hu، Simona Liao، Shengjie Ma، Elsie Nallipogu و Nick Trogh به خاطر بازخوردها، بینش‌ها و مشارکت‌های ارزشمندشان در این پست وبلاگ.

ما عمیق‌ترین سپاس خود را نثار جامعهٔ توسعه‌دهندگان خود می‌کنیم که با بازخوردهای مستمرشان، ما را به ارائهٔ بهترین تجربهٔ ممکن با VS Code و GitHub Copilot ترغیب می‌کنند. تشکر فراوان از محققان، مهندسان، مدیران محصول و طراحان در سراسر GitHub و Microsoft که داده‌های آموزشی، پایپ‌لاین آموزشی، مجموعه‌های ارزیابی و پشتهٔ سرویس‌دهی را مدیریت و آماده کردند، و همچنین از تیم‌های VS Code و GitHub Copilot برای انتشار روان مدل‌ها.

کدنویسی خوبی داشته باشید! 💙

نظر مهندس بهمن آبادی: موفقیت مدل‌های ترکیبی هوش مصنوعی در کدنویسی، مرهون هم‌تکاملی همزمان داده‌ها، معماری پچ و طراحی کلاینت است؛ جایی که تبدیل وظایف مجزا به ساختار واحد Diff Patch، کلید اصلی کاهش تاخیر و افزایش دقت ویرایش‌هاست.

منبع: https://code.visualstudio.com/blogs/2026/09/16/building-the-github-copilot-inline-suggestions-model-part-one/?utm_source=vscode-devblogs&utm_medium=article&utm_campaign=devblogs-rss