نکات کلیدی
- برای بررسی اینکه آیا ابزارهای فعلی مبتنی بر LLM میتوانند به افزایش بهرهوری برنامهنویسان کمک کنند یا نه، یک آزمایش با استفاده از افزایش پوشش کد در تستهای واحد بهعنوان یک معیار عینی انجام شد.
- در این آزمایش فقط LLMهای رایگان انتخاب شدند؛ شامل ChatGPT، CodeWhisperer، codellama:34b، codellama:70b و Gemini. همه اینها نسخههای بدون هزینه هستند و به همین دلیل GitHub Copilot در این فهرست قرار نگرفت.
- یک آزمایش طراحی شد تا توانایی هر یک از LLMهای انتخابشده در تولید تست واحد برای یک سرویس وب از قبل پیادهسازیشده و غیرساده بررسی شود. به همه این LLMها دقیقاً یک مسئله و یک پرامپت یکسان داده شد. سپس خروجی هرکدام با پروژه متنباز موجود ادغام شد، پروژه کامپایل شد و تستهای واحد اجرا شدند. تمام اصلاحاتی که برای عبور موفق build لازم بود، ثبت شد.
- هیچکدام از LLMها بدون نظارت و دخالت انسان نتوانستند این کار را بهطور کامل و موفق انجام دهند، اما بسیاری از آنها توانستند تا حدی فرآیند نوشتن تست واحد را سریعتر کنند.
هنوز حتی دو سال هم از زمانی که OpenAI، ChatGPT را معرفی کرد نگذشته است؛ اولین مدل زبانی بزرگ جریان اصلی مبتنی بر ترنسفورمر که به شکلی بسیار ساده و قابل استفاده در اختیار عموم قرار گرفت.
این عرضه موجی از هیجان و فعالیت را از والاستریت تا کاخ سفید به راه انداخت. تقریباً هر شرکت Fortune 500 و هر استارتاپ فناوری در تلاش است بفهمد چگونه میتواند از LLMها بهرهبرداری کند. اکوسیستم توسعهدهندگان پر از ابزارها و زیرساختهای پشتیبان، مانند LangChain، شده است که یکپارچهسازی برنامههای موجود با LLMها را تسریع میکنند.
در یک کنفرانس فناوری اخیر در دنور، Eric Evans (خالق Domain-Driven Design) «همه را تشویق کرد که از همین حالا شروع به یادگیری LLMها کنند، آزمایش انجام دهند و نتایج و آموختههای این آزمایشها را با جامعه به اشتراک بگذارند».
آزمایش
من تصمیم گرفتم آزمایشی طراحی کنم که بتوانم آن را روی هر یک از LLMهای رایج موجود اجرا کنم تا بتوانم آنها را با هم مقایسه کنم و مرزهای توانایی LLMها را دستکم در کوتاهمدت بررسی کنم. من علاقهای ندارم یا نگران این نیستم که LLMها چگونه قرار است برنامهنویسان را جایگزین کنند. هنگام استفاده از LLMها همچنان به توسعهدهندگان باتجربه نیاز دارید، چون باید در بازبینی پیشنهادها بسیار هوشیار باشید. علاقه اصلی من این است که ببینم LLMها چگونه میتوانند با خودکارسازی بخشهای زمانبر، تکراری و در عین حال بسیار مهمِ کدنویسی، بهرهوری برنامهنویسان را افزایش دهند. منظورم، مشخصاً، تستهای واحد است.
بسیاری ادعا میکنند که تستهای واحد جای مناسبی برای LLMها نیستند، چون از نظر تئوری، در توسعه مبتنی بر تست (TDD) ابتدا تستها نوشته میشوند و بعد کد. من در شرکتهای متعددی تجربه داشتهام که در آنها تستهای واحد تقریباً به یک فکر بعدی تبدیل شده بودند و میزان پوشش کد توسط تستها مستقیماً با مقدار زمان باقیمانده در اسپرینت نسبت داشت. اگر برنامهنویسان بتوانند با کمک LLMها سریعتر تست واحد بیشتری بنویسند، پوشش کد افزایش پیدا میکند و کیفیت بالاتر میرود. همین حالا هم میتوانم صدای اعتراض طرفداران دوآتشه TDD را بشنوم. متأسفم، اما این واقعیت سرد و سخت ماجراست. ضمن اینکه چطور میخواهید وابستگیهای خارجی را در تستها mock کنید بدون اینکه ابتدا جزئیات داخلی پیادهسازی را بدانید؟
من یک مخزن متنباز دارم که در آن مایکروسرویسهای یکسان را با زبانها و پشتههای فنی مختلف پیادهسازی میکنم. هر پیادهسازی شامل دسترسی به MySQL است که جلوی آن Redis قرار دارد. هر مایکروسرویس را تحت یک تست بار یکسان قرار میدهم و دادههای عملکردی را جمعآوری و تحلیل میکنم تا مقایسه انجام شود. من یک کلاس سرویس از پیادهسازی Java با Spring Boot را انتخاب کردم و همه متدهای عمومی و قابل مسیردهی آن را بهجز سه متد حذف کردم. سپس کد تست واحد را گرفتم و همه تستها را بهجز یکی حذف کردم. importها، setup و تزریق وابستگی مبتنی بر annotation را دستنخورده باقی گذاشتم. در پرامپت از LLM خواستم دو تست واحد دیگر را تولید کند. کل پرامپت حدود ۲۵۰ خط (نزدیک به ۱۳۰۰ کلمه) طول داشت. چرا Java با Spring Boot را انتخاب کردم؟ چون حجم زیادی کد آزاد و بدون محدودیت لایسنس بهصورت آنلاین وجود دارد که بخشی از دادههای آموزشی LLMها بوده است. رویکرد Spring Boot بهشدت مبتنی بر annotation است و درک عمیقتری نسبت به صرفاً خواندن کد میطلبد.
من با این مسئله مثل یک آزمایش علمی برخورد کردم، اما این آزمایش چندان خوب نیست. یک آزمایش فقط زمانی ارزشمند است که قابل تکرار باشد، اما همه فناوریهایی که در اینجا ارزیابی شدهاند دائماً در حال تغییرند و سازمانهای مالک آنها هزینه زیادی برای نوآوری و امیدوارانه بهبودشان صرف میکنند. بسیار بعید است اگر امروز همان آزمایش را تکرار کنید، به نتایج یکسانی برسید. با این حال، یک نسخه از پرامپتی که استفاده کردم را اینجا نگه داشتهام، اگر خواستید خودتان امتحان کنید.
ChatGPT از OpenAI
در روزهای ابتدایی ChatGPT، در اواخر ۲۰۲۲، مدلهای زبانی بزرگ مبتنی بر ترنسفورمر و OpenAI وارد مرکز توجه رسانهها شدند. طبیعتاً باید ChatGPT را در این آزمایش قرار میدادم. در آن روزهای ابتدایی مشکلات زیادی وجود داشت: پنجره context کوچک، خروجی با کیفیت پایین، فراموشکردن پرامپت، و توهمات با اعتمادبهنفس بالا. حالا وضعیت خیلی بهتر شده است. مثل سایر فناوریهای ارزیابیشده، من از نسخه رایگان استفاده کردم. نگرانیهایی درباره افشای اطلاعات مالکیتی از طریق نشت پرامپت هنگام استفاده از LLMهای تجاری وجود دارد. به همین دلیل آزمایش را بر اساس کد متنباز انجام دادم. چیزی محرمانهای برای نشت وجود نداشت. نشت پرامپت اجتنابناپذیر است، چون پرامپتها برای fine-tune کردن مدل استفاده میشوند و در طول زمان کیفیت پاسخها را بهبود میدهند.
عملکرد ChatGPT چگونه بود؟ بد نبود. توضیح نتایج مختصر و دقیق بود. خروجی مفید بود، اما باگهایی داشت، مخصوصاً در بخش تزریق وابستگی و mocking. پوشش تست متوسط بود. کد تست واحد assertionهایی برای ویژگیهای تکی، not found و not null داشت. با وجود باگها، همچنان خروجی را مفید میدانستم، چون احساس میکردم اصلاح باگهای کد تولیدشده زمان کمتری نسبت به نوشتن کل کد از ابتدا میگیرد.
Amazon CodeWhisperer
فناوری بعدی که به این شکل ارزیابی کردم Amazon CodeWhisperer بود. این یک افزونه برای Visual Studio Code است. تجربه کاربری آن اساساً شبیه تکمیل بهتر دستورات است. شما شروع به تایپ میکنید و ابزار خط را کامل میکند؛ گاهی حتی یک بلاک کد را. سپس تصمیم میگیرید پیشنهاد را بپذیرید یا رد کنید. بیشتر مواقع پیشنهاد را میپذیرفتم و بعد اصلاحات لازم برای رفع باگها را انجام میدادم. با CodeWhisperer بیشترین احساس بهرهوری را داشتم.
به نظرم CodeWhisperer از نظر رویکرد شبیه GitHub Copilot است که آن را ارزیابی نکردم چون هزینه دارد، در حالی که CodeWhisperer رایگان بود. درباره جزئیات داخلی CodeWhisperer، آمازون اطلاعات زیادی منتشر نمیکند. احتمالاً چیزی فراتر از یک LLM صرف است، اما حس LLM را دارد. گمان میکنم CodeWhisperer دائماً با سرور ارتباط برقرار میکرد، چون IDE اغلب قفل میشد. با غیرفعالکردن CodeWhisperer، IDE دوباره پاسخگو میشد.
Code Llama
Ollama یک پلتفرم متنباز است که امکان ساخت و اجرای LLMها را فراهم میکند. این پلتفرم تعداد قابل توجهی مدل متنباز از پیش آموزشدیده را ارائه میدهد، از جمله مدل Code Llama از Meta. علاقه زیادی به این LLMهای متنباز وجود دارد. سرمایهگذار مشهور Bill Gurley در یک نشست در UCLA سال گذشته، Llama 2 (که در کتابخانه مدلهای Ollama وجود دارد) را بزرگترین تهدید OpenAI معرفی کرد. یادتان هست به موضوع نشت پرامپت اشاره کردم؟ چون Ollama را روی ماشینهای مجازی تحت کنترل مستقیم خودتان اجرا میکنید، احتمال نشت پرامپت بسیار کم است.
هرچند اجباری نیست، اما واقعاً بهتر است این مدلها را روی سیستمی با GPU نسبتاً قدرتمند اجرا کنید. من روی لپتاپ شخصیام GPU ندارم، بنابراین یک ماشین لینوکسی مخصوص یادگیری ماشین با CUDA روی VM نوع a2-highgpu-1g با کارت nvidia-tesla-a100 (۳۱۲ TFLOPS) در Google Cloud Platform راهاندازی کردم. مشخصاً از مدل codellama:34b استفاده کردم. طبق بلاگ متا که این مدل را معرفی کرده: «Code Llama نسخهای تخصصی برای کدنویسی از Llama 2 است که با آموزش بیشتر روی دادههای کدنویسی ساخته شده و دادههای بیشتری از همان مجموعه برای مدت طولانیتر نمونهبرداری شدهاند.» من همین آزمایش را با codellama:70b روی NVIDIA A100 با حافظه ۴۰ گیگابایت هم اجرا کردم و بهبود جزئی در پوشش کد دیدم. هزینه آن ماشین در زمان نگارش این مطلب، ساعتی ۳٫۶۷ دلار بود.
نتایج کمی بدتر از ChatGPT بودند، اما خیلی هم بد نبودند. خطاهای کامپایل، پکیجها و importهای گمشده، و باگهای mocking و تزریق وابستگی وجود داشت. با مدل 34b تنها پوشش کد این بود که not null بررسی شود. با مدل 70b این مورد با assertionی جایگزین شد که بررسی میکرد مقدار بازگشتی از فراخوانی سرویس با مقداری که در mock مربوط به DAO تزریق شده برابر است. در نتایج Code Llama توضیح یا ارجاع آنلاین وجود نداشت. خروجی تولیدشده شامل کامنتهایی در کد بود که ارزش زیادی اضافه نمیکردند.
Google Gemini
آخرین LLM که این آزمایش را روی آن انجام دادم Gemini بود که نام جدید Bard گوگل است. مشابه Code Llama، خروجی تولیدشده شامل package statement یا importهایی که در ورودی وجود داشتند نبود. اصلاح این موضوع ساده بود. مطمئن نیستم این یک اشتباه بود یا صرفاً تفسیر متفاوتی از درخواست من. مثل همه ابزارهای مبتنی بر چت، خروجی باگهای مشابهی در تزریق وابستگی و mocking داشت. پوشش کد کمی بهتر بود، چون تستهایی برای cache hit و cache miss هم وجود داشت. توضیح ارائهشده کمی بهتر از ChatGPT بود و منبعی را که بیشتر استفاده کرده بود ذکر میکرد، هرچند آن منبع مخزن متنبازی نبود که تمام کدهای آزمایش از آن آمده بودند. این خروجی هم به همان شکلی مفید بود که ChatGPT مفید بود: زمان رفع باگها کمتر از نوشتن دو متد از صفر بود.
نتیجهگیری
بهدستآوردن اندازهگیریهای عددی دقیق و قابل اعتماد برای چیزی بهشدت ذهنی مثل کیفیت نرمافزار، کار دشواری است. جدول زیر تلاش میکند یافتهها را به شکلی عددی و قابل مقایسه خلاصه کند. الگوریتم Myers Diff برای اندازهگیری تعداد خطوط کد اضافهشده و حذفشده (یک خط اصلاحشده هم بهعنوان اضافه و هم حذف شمارش میشود) استفاده شده است؛ این تعداد خطوط همان اصلاحاتی هستند که برای رفع باگهای کد تست واحد تولیدشده لازم بودهاند (چون قطعاً مجبورید کد تولیدشده را اصلاح کنید). پوشش کد Jacoco درصد دستورالعملها (در عمل بایتکد جاوا) است که توسط تستهای واحد پوشش داده شدهاند، تقسیم بر کل دستورالعملها.
خلاصه نتایج تولید تست واحد با LLMها (اعدادی)
| ابزار هوش مولد | تحلیل توضیحی | الگوریتم Myers Diff | پوشش کد Jacoco |
|---|---|---|---|
| ChatGPT | بله | ۸ | ۲۹.۱۲% |
| CodeWhisperer | خیر | ۲۶ | ۲۷.۸۱% |
| codellama:34b | خیر | ۱۱۷ | ۲۳.۴۲% |
| Gemini | بله | ۶۹ | ۳۱.۲۳% |
از این آزمایشها کاملاً مشخص شد که هیچ هوش عمومی مصنوعی در تولید این کدهای تست واحد وجود نداشت. نبود درک حرفهای از تزریق وابستگی مبتنی بر annotation و mocking برای من روشن کرد که پشت پرده، چیز عمیقاً هوشمندی وجود ندارد. خیلی ساده، یک LLM تعداد زیادی سند را کدگذاری میکند، ورودی را به توکنها میشکند و با استفاده از یک شبکه عصبی مبتنی بر ترنسفورمر با تعداد زیادی وزن (که همان مدل است)، زمینه را ثبت میکند.
وقتی سؤالی پرسیده میشود (مثلاً یک تمرین برنامهنویسی)، مدل با پیشبینی محتملترین ادامه یا تکمیل ورودی پاسخ تولید میکند. زمینه ارائهشده در ورودی را در نظر میگیرد و پاسخی منسجم، مرتبط و متناسب با زمینه تولید میکند، اما لزوماً درست نیست. از این منظر، میتوانید LLMها را نوعی جستوجوی پیچیده و مبتنی بر زمینه در نظر بگیرید؛ البته بسیار پیشرفتهتر از جستوجوی مبتنی بر IDF معکوس یا PageRank در موتورهای جستوجوی وب یا Lucene حوالی ۲۰۲۰.
آنچه در LLMهای این آزمایش دیدم، یک قابلیت جستوجوی کد بسیار خوب بود که برای یک توسعهدهنده باتجربه مفید است. چیزی که در ChatGPT، Ollama و Gemini برایم ناامیدکننده بود این است که من شرطی شدهام انتظار هوش انسانی را در آنسوی یک پنجره چت داشته باشم. اما چنین انتظاری از تکمیل دستور ندارم. CodeWhisperer ناامیدم نکرد، نه به این دلیل که هوش مصنوعیاش بهتر بود، بلکه چون تجربه کاربریاش بهتر توانست انتظارات مرا مدیریت کند.
قدم بعدی چیست؟
چند نگرانی سازمانی وجود دارد که احتمالاً قبل از خروج ابزارهای تولید تست واحد مبتنی بر هوش مولد از فاز آزمایشی باید به آنها رسیدگی شود.
قبلاً درباره نشت پرامپت صحبت کردم. این باید یک مسئله بزرگ برای سازمانها باشد، چون بخش زیادی از کد سازمان باید در پرامپتها وارد شود و اغلب سازمانها کد خود را مالکیتی میدانند. اگر پرامپتها به یک نمونه مدل مشترک با سازمانهای دیگر برنگردد، نگرانی نشت پرامپت به دیگران وجود ندارد. یک گزینه اجرای محلی LLM (مثل Ollama) است که مستلزم آن است هر توسعهدهنده ماشینی با GPU قدرتمند داشته باشد. گزینه دیگر، اشتراک یک نسخه تکمشتری (single-tenant) و غیرمشترک از ChatGPT یا Gemini است. گزینه سوم غیرفعالکردن کامل fine-tune شدن مدل بر اساس پرامپتهاست. در حال حاضر گزینه سوم در دسترس است، اما گزینه دوم نه.
نگرانی دیگر هزینه است. بهنظر میرسد قیمتگذاری هوش مولد امروز بیشتر بر افزایش سهم بازار متمرکز است و هنوز همه هزینهها را پوشش نمیدهد. برای گذار از رشد به سودآوری، این قیمتها ناچاراً افزایش خواهند یافت. همان NVIDIA A100 با حافظه ۴۰ گیگابایت که در بخش Code Llama به آن اشاره کردم، امروز حدود ۱۰ هزار دلار قیمت دارد. همچنین مسئله مصرف انرژی مطرح است. اگرچه نوآوری در این حوزه ادامه دارد، اما معمولاً GPUها حدود سه برابر CPUها برق مصرف میکنند. رویکرد تکمشتری امنتر است، اما گرانتر هم هست، چون فروشنده نمیتواند از صرفهجوییهای مقیاس خارج از ساخت مدل پایه بهرهمند شود. افزایش بهرهوری بهدستآمده هم فقط جزئی بود. بنابراین هزینه قطعاً در معادله استفاده بلندمدت نقش خواهد داشت.
