1 417
订阅者
无数据24 小时
-17 天
-430 天
帖子存档
1 417
לא צריך לחכות ל 120 אלף שורות
הפוסט הזה מרדיט קלע לדעתי בול לנקודה של הוייב. תקציר ותרגום שלי-
> אני מתכנת Front End עם 14 שנות ניסיון ולאחרונה התחלתי לכתוב משחקים לבד, רק בעזרת AI. בהתחלה חשבתי שאתן ל AI לכתוב רק כדי לראות שדברים עובדים אבל מהר מאוד התמכרתי. הקוד היום מכיל כ 120 אלף שורות ונראה זוועה - אי אפשר להוסיף פיצ'רים חדשים, קוד משוכפל בכל מקום, באגים שלא ניתנים לתיקון ואפילו אלה שאפשר לתקן רק יגרמו לקוד להסתבך עוד יותר.
תיאור הקשר הגורדי של הקוד מאפיין פרויקטים רבים בתעשייה, אבל מה שמעניין כאן זה שהבן אדם הצליח ליצור קוד ספגטי שלאחרים היה לוקח שנים לכתוב תוך כמה שבועות בלבד. זה הכח של ה Vibe.
אבל האמת שאין חדש תחת השמש. כמתכנתים תמיד כתבנו קוד גרוע ותכננו לשכתב אותו יום אחד כשיהיה זמן, ואף פעם לא היה לנו זמן. הבעיה היא רק שעכשיו אנחנו מגיעים לרגע של ה Great Refactor הרבה יותר מהר, כי ה AI כותב קוד הרבה יותר מהר ממה שאי פעם יכולנו.
את הפיתרון אנחנו כבר מכירים: לא מחכים ל 120 אלף שורות אלא משכתבים תוך כדי תנועה. AI כותב 3-4 קבצים ואז אני מארגן מחדש את הקוד בצורה מסודרת, אחרי זה אני יוצר כמה אבסטרקציות חדשות ונותן ל AI לרוץ איתן כדי לכתוב עוד 5 קבצים וככה ממשיכים יחד, אני חושב וה AI כותב. רק שיתוף פעולה כזה יאפשר לנו לבנות מערכות יציבות לאורך זמן.
1 417
חלומות על VPS
טכנולוגיות וירטואליזציה לשרתים בשילוב מערכת הפעלה לינוקס שהיתה בקוד פתוח העלו את הפופולריות של שכירת "שרתים" וירטואליים במחירי ריצפה. דיגיטל אושן קמה ב 2011 ואיפשרה לכל אחד לשכור שרת לינוקס ב 5$ לחודש. מאז ובזכות בסיסי נתונים בקוד פתוח ושפות תכנות ופריימוורקים פתוחים כולנו יכולים לעבוד בענן בזול וגם להתקין מערכות דומות בחינם על מחשבי הפיתוח שלנו. ב 2013 דוקר הצטרף למסיבה ותוכנות התחילו לרוץ בתוך קונטיינרים.
ב 2014 אמזון מפרסמים את Lambda ואנחנו גילינו שכן אנחנו יכולים להתקין ולהריץ הכל על מכונה וירטואלית אחת אבל כשצריך לעשות דברים בסקייל יותר קל לכתוב פונקציית Lambda או להשתמש בסרביס של תור הודעות או סרביס של שליחת מייל. האינטגרציה בין השירותים השונים שכנעה רבים לוותר על ניהול השרת המקומי.
ועכשיו עם ה AI נראה שמודל הענן מתרחב - לא רק שנצטרך לשלם לכל דקת חישוב על הקוד שלנו, הרכיב הבא בתשתית יחייב בתשלום לפי טוקנים למודל ה AI התורן. וכל זה בתקופה שיש אינסוף מודלים חינמיים בקוד פתוח, שאי אפשר להריץ כי הם דורשים יותר מדי משאבים.
לא בטוח מה תהיה הקפיצה הבאה בפיתוח ה AI, אבל מה שהייתי רוצה לראות היום זה דרך להוריד את מחירי המודלים כדי שאפשר יהיה להשתמש במודלי הקוד הפתוח ובמחירים נמוכים משמעותית ממה שאנחנו רואים במודלים המסחריים. לא למשחקים וניסויים כמו שאני יכול להריץ על gradio, אלא ממש שרת שאני יכול בלחיצת כפתור להתקין עליו מודל קוד פתוח ולחבר למערכת שלי וכל זה במחיר קבוע של 5$ לחודש.
וכן Docker Model Runner הוא צעד טוב בכיוון ואני מקווה שיגיע לעוד מערכות הפעלה, אבל אני חושש שהסיפור הגדול עם LLM מחוץ לענן הוא החומרה, ואת זה לא ברור איך לפתור.
מתי נראה את ה Digital Ocean הראשונה למודלי קוד פתוח? ואיזה קפיצה טכנולוגית עוד צריך כדי להגיע לשם?
1 417
# באג או פיצ׳ר?
פותח פרויקט ריילס חדש, נכנס מהכרום במצב מובייל ו-בום. שגיאת 406 עם הטקסט Not Acceptable.
עכשיו אני מודה אני לא רגיל לשגיאת 406 בהקשר הזה. בדרך כלל שולחים 406 כשדפדפן מבקש סוג קובץ שהשרת לא יודע לייצר כלומר שיש בעיה בכותרות Accept, Accept-Encoding או Accept-Language. פה לא נגענו בכותרות ה Accept.
מסתבר ש Rails 7.2 הוסיפו מנגנון הגנה חדש שמגביל גישה לאתר רק לדפדפנים מודרניים. פרטים כאן:
[https://guides.rubyonrails.org/7_2_release_notes.html#add-browser-version-guard-by-default](https://guides.rubyonrails.org/7_2_release_notes.html#add-browser-version-guard-by-default)
למרות שאני מבין מאיפה זה בא חייב להודות שזה נראה יותר כמו באג מאשר כמו פיצ'ר. המגמה היום בפיתוח יישומי ווב היא לבנות יישומים פרוגרסיביים כלומר כאלה שיצליחו להיטען בכל דפדפן, גם אם על חלק מהדפדפנים החוויה תהיה פחות טובה, ולשפר את החוויה ככל שהדפדפן מתקדם יותר. הבחירה לזרוק החוצה דפדפנים ישנים כברירת מחדל בלי קשר לתוכן האתר, כולל לאתרים שכלל לא משתמשים ב JavaScript נראית תמוהה ואני מקווה שתשתנה בעתיד.
בינתיים אני מוסיף לרשימת ה Best Practices כשפותח פרויקט ריילס חדש למחוק את שורת ההגבלה מהקובץ
app/controllers/application_controller.rb כלומר את השורה:
class ApplicationController < ActionController::Base
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
# Delete this line to allow everyone to access the site
allow_browser versions: :modern
end1 417
כתיבת AI Agent שמשתמש בכלים עם Vercel AI SDK
בפוסט אתמול הצגתי סוכן AI מאוד פשוט שרץ כל יום ומעצב אימייל עם סיכום מידע שעניין אותי מגיטהאב. בואו ניקח את הקוד צעד קטן קדימה ונתן לסוכן שלנו גישה לכלים כך שיוכל להפעיל קוד וכך לקבל החלטות בצורה יותר עצמאית.
הפעלת כלים מתוך קוד שניגש ל LLM זה בסך הכל בקשה ל LLM להדפיס קוד מיוחד כשהוא מרגיש שצריך להפעיל כלי, זיהוי הקוד הזה מתוך הסוכן שפנה ל LLM ואז הסוכן מפעיל את הכלי ופונה שוב ל LLM עם התוצאה להמשך עבודה. כתבתי בעבר איך לממש לבד מנגנון הפעלת כלים ב AI. היום לא אבנה את המנגנון מאפס אלא אשתמש בספריה קיימת להפעלת כלים של Vercel שנקראת פשוט AI SDK. הספריה של ורסל מתפתחת מהר וכוללת הרבה יכולות ולדעתי היא הדרך הקלה ביותר לכתוב סוכני AI בטייפסקריפט.
מה אנחנו בונים
הסוכן היום יעזור לכם ליצור נוכחות במדיה חברתית - הוא מזהה דיונים מעניינים ברדיט על תכנות ומציע רעיונות לתגובות שאנחנו יכולים לכתוב לדיונים אלה. בנוסף הוא מציע רעיונות לפוסטים שאפשר להוסיף ב subreddits שהוא סוקר.
בשביל לעבוד הסוכן ישתמש בשני כלים:
1. כלי שמוציא את כל הדיונים מ Subreddit.
2. כלי שמוציא את כל ה Comments על פוסט.
זכרו ש"שימוש בכלים" זה בסך הכל הפעלת פונקציה. אנחנו מעבירים את ה"כלי", כלומר את הפונקציה, כפרמטר לפונקציה של AI SDK, והפריימוורק פונה ל LLM, מפענח את התשובה, מזהה בקשה להפעלת כלים, מפעיל את הפונקציה ופונה שוב ל LLM להמשך עבודה. אנחנו רק צריכים להגביל את מספר הפעמים שהלולאה הזו תתבצע באמצעות עוד פרמטר שנעביר לפונקציה של AI SDK.
הקוד
זה קוד הסוכן, בדוגמה בתור סקריפט שרק מדפיס את התוצאה למסך:
import { z } from 'npm:zod';
import { generateObject, tool, generateText } from 'npm:ai';
import { openai } from 'npm:@ai-sdk/openai';
interface RedditComment {
author: string;
body: string;
score: number;
created_utc: number;
replies?: RedditComment[];
}
interface RedditPost {
title: string;
author: string;
url: string;
permalink: string;
created_utc: number;
num_comments: number;
score: number;
}
async function getLatestDiscussions({subreddit, limit = 10}: {subreddit: string, limit?: number}): Promise<RedditPost[]> {
const url = \https://www.reddit.com/r/${subreddit}/new.json?limit=${limit}\;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(\Error fetching subreddit: ${response.statusText}\);
}
const data = await response.json();
const posts: RedditPost[] = data.data.children.map((child: any) => ({
title: child.data.title,
author: child.data.author,
url: child.data.url,
permalink: \https://www.reddit.com${child.data.permalink}\,
created_utc: child.data.created_utc,
num_comments: child.data.num_comments,
score: child.data.score,
}));
return posts;
} catch (error) {
console.error('Failed to fetch discussions:', error);
return [];
}
}
function parseComment(comment: any): RedditComment {
return {
author: comment.data.author,
body: comment.data.body,
score: comment.data.score,
created_utc: comment.data.created_utc,
replies: comment.data.replies
? comment.data.replies.data.children
.filter((child: any) => child.kind === "t1")
.map(parseComment)
: [],
};
}
async function getPostComments({permalink}: {permalink: string}): Promise<RedditComment[]> {
const url = \https://www.reddit.com${permalink}.json\;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(\Error fetching comments: ${response.statusText}\);
}
const data = await response.json();
const comments = data[1].data.children;
const parsedComments = comments
.filter((item: any) => item.kind === "t1")
.map(parseComment);
return parsedComments;
} catch (error) {
console.error("Failed to fetch post comments:", error);
return [];
}
}
const tools = {
latestDiscussions: tool({1 417
הזדמנות ללמידה
ומה אם AI היה יכול ליצור קוד שאפשר ללמוד ממנו? מה אם ההזיות היו פועלות לטובתנו? זה אפשרי אבל יש לכך מחיר.
לא מזמן קראתי מאמר שממליץ להימנע משימוש בפריימוורקים בעבודה עם AI. ההגיון שם היה שיש מספר תכונות לפריימוורק שהופכות את השילוב שלה עם AI ליותר מאתגר:
1. גירסאות חדשות של פריימוורק נוטות לשבור תאימות אחורה, ונתוני האימון של ה AI כוללים דוגמאות קוד גם של גירסאות ישנות.
2. יותר גרוע - גרסאות חדשות של פריימוורק נוטות לשנות דרכי עבודה מומלצות ו Best Practices. בגלל שנתוני האימון של ה AI מכילים גם את השיטות הישנות, עלינו להיות חשדנים ב AI במקומות האלה.
3. שימוש לא נכון בפריימוורק עלול להשפיע לרעה על כל האפליקציה - לדוגמה בריאקט מימוש קוד עם חלוקה לא נכונה לקומפוננטות יוביל למערכת שתהיה יותר קשה לתחזוקה.
4. פריימוורק נועד לתת לי מבנה טוב לאפליקציה מלאה. לפריימוורק יש הגיון פנימי שה AI נוטה להזות ולפספס.
מכל הסיבות האלה כשאני מבקש מ AI לייצר עבורי קוד ריאקט אני צריך לקרוא כל שורה בקוד, לתקן את העבודה שלו, למחוק חלקים ולתת לו ליצור מחדש. זה עדיין חוסך לי זמן, במיוחד אם אני יודע מתי להפעיל את ה AI ומתי לכתוב בעצמי, אבל זה לא משהו שהייתי רוצה ללמוד ממנו.
לעומת זאת ברגע שמוציאים את הפריימוורק והספריות מהתמונה אנחנו יכולים להרוויח הזדמנות למידה. לדוגמה הפרומפטים הבאים:
1. בנה לי משחק טטריס מאפס שישתמש ב Canvas בלי שימוש באף ספריה חיצונית.
2. בנה רשימת פריטים ניתנים לגרירה וסידור מחדש ב Vanilla JavaScript בלי ספריות חיצוניות.
3. בנה תשתית Single Page Router שמאפשרת לעבור בין דפים שונים באפליקציה ב Vanilla JavaScript ובלי ספריות חיצוניותץ
פרומפטים כאלה מאפשרים לנו לקבל קוד שאפשר לקרוא ואפילו ללמוד ממנו איך מנגנון מסוים עובד. כשאין תלויות חיצוניות נתוני האימון של ה AI עובדים לטובתנו וסכנת ההזיות יורדת משמעותית.
1 417
מה שטוב בשבילי / מה שטוב למערכת
שני הדברים האלה הולכים יחד לעתים הרבה יותר קרובות ממה שנדמה, אפילו כשקשה לראות את זה בתוצאה הסופית:
1. קל לי יותר לפתח כשאני עושה קומיט כל כמה שינויים. במבט על המערכת קל יותר לקרוא את ההיסטוריה כשיש בה מעט קומיטים עם הודעות בעלות משמעות.
2. קל לי יותר לפתח כשאני מתעלם ממקרי קצה ומתקדם ב Happy Path לפני שאני חוזר לטפל בכל המקרים. חשוב יותר ברמת המערכת לוודא ששום באג לא יגיע לפרודקשן שעלול לפגוע באבטחה או בסקייל של המערכת בעולם האמיתי.
3. קל לי יותר לפתח עם הודעות Debug כדי שאראה מה קורה. טוב למערכת שהמוצרים לא יכללו הודעות דיבג אלא רק מידע רלוונטי למשתמשים.
4. קל לי יותר לקבל כל הצעה של ה AI בלי לקרוא את הקוד, טוב יותר למערכת שהקוד שנכנס עבר ביקורת ולא כולל שטויות או המצאות.
המעבר מ"מה שטוב בשבילי" ל"מה שטוב למערכת" הוא המעבר לפרודקשן. זה לא קשור לכמה לקוחות או כמה עובדים יש לנו. המעבר לפרודקשן הוא קודם כל בחירה, בחירה להוסיף עוד שלב לתהליך הפיתוח ובחירה להסתכל על העבודה כמוצר ולא כתחביב.
1 417
שלוש ביקורות שמצאתי ברשת נגד פיתוח בעזרת AI
פיתוח בעזרת כלי AI הוא עובדה מוגמרת. כשחיפשתי ברשת חוות דעת שליליות על שיטת עבודה זו מצאתי הרבה תלונות על כלים ספציפיים - למשל "למה עזבתי את קופיילוט בשביל קרסר" או "תתקינו זד ותוכלו להפסיק לשלם על קרסר", אבל מעט מאוד ביקורות רציניות נגד שיטת העבודה באופן כללי. בכל זאת יש שלוש ביקורות שחוזרות על עצמן בצורות שונות ואני רוצה שנשים אותן כאן על השולחן:
פגיעה במיומנות
רעיון ראשון הוא שתכנות זה כיף, זו מיומנות, זה מקצוע, ועבודה בעזרת כלי AI פוגעת במקצועיות שלנו כמפתחים ולטווח הרחוק תשאיר אותנו מאחור ואת המערכות שאנחנו בונים ברמה נמוכה.
בין הדברים שעלו:
1. אחרי שימוש בכלי השלמה מבוססי AI שכחתי איך לכתוב אפילו פונקציה בסיסית.
2. התרגלתי לתת ל AI לפתור בעיות וכך הפסקתי להנות מתכנות.
3. קבלת החלטות קטנות במהלך כתיבת הקוד עוזרת לי לקבל החלטות גדולות לגבי הארכיטקטורה ולהרגיש את המערכת.
במבט ראשון אני רוצה לפסול את הטיעונים האלה מתוך מחשבה שיש מיומנויות שאנשים כבר לא צריכים וזה בסדר, ואולי פיתוח קוד זו עוד מיומנות שאין לה עתיד. אבל אז אני מנסה לתת ל AI לכתוב קוד ומבין שהוא פשוט עדיין לא מספיק טוב בזה. כל מי שרוצה שבני אדם יפסיקו לכתוב קוד מתבסס על איזו פריצת דרך עתידית או על המשך מגמת ההתפתחות בכלי ה AI שראינו בשנים האחרונות. אולי זה יקרה ואולי לא, כרגע לדעתי עדיין מוקדם לדעת.
תמונת מציאות חלקית
ביקורת שניה נגד כלי AI היא שה AI חושף תמונת מציאות מסוימת ולרוב חד מימדית. בעוד שחיפוש בגוגל יכול להציג מגוון של דעות במגוון אתרים, שליחת שאלה ל AI תחזיר תשובה אחת.
גישת התשובה האחת בעייתית ממגוון סיבות:
1. המציאות מורכבת וכדי לגבש דעה רצוי שבני אדם יהיו חשופים למגוון רעיונות ודעות. כן גם בקוד.
2. האילוצים והמערכת שאני עובד עליה יכולים להיות שונים מהדעה "הפופולרית". מה עושים כשה AI מתעקש שאתה לא בכיוון, למרות שאתה יודע שהוא טועה? והאם גם בעוד 5 שנים יהיה מי שיתווכח עם ה AI ויבחר להתעלם מההמלצות שלו?
3. תמונת מציאות חלקית עשויה להביא לתוצאה לא אידאלית בקוד שתיתפס בתור אמת. זה יכול להיות שיטת שימוש בספריה מסוימת, דרך מסוימת לשמור מידע בטבלאות או העדפה לספריה מסוימת. זה קצת כמו אנשים שנכנסו תמיד לתוצאה הראשונה בגוגל, רק שהפעם אין לנו אפילו את דף התוצאות.
4. ככל שאנחנו חשופים פחות למגוון אנחנו מתאמצים פחות בבחינה טכנולוגית וקבלת החלטות שמתאימה לתנאים הספציפיים שלנו. אם ה AI תמיד עונה בריאקט אז כדאי גם לי להשתמש בריאקט, מה שיוצר מעגל הרסני כלפי ספריות אחרות שרק יהיו פחות ופחות פופולריות.
חוב טכני
הביקורת החשובה ביותר לדעתי נגד AI היא החוב הטכני שקוד מבוסס AI נוטה לצבור. קוד של AI בממוצע יוצא ארוך יותר מקוד מקביל של מתכנתים אנושיים, כולל פחות אבסטרקציות ומיועד "לפתור את הבעיה" כאן ועכשיו. האבסורד הוא שבטכנולוגיה הנוכחית ל AI אין יכולת לתחזק את הקוד שהוא עצמו כותב.
פעם בצוותים אנושיים היינו מדברים על אנשים שכותבים Quick and Dirty ולכן אחרי שנה או שנה וחצי המוצר נתקע וכמו כדור גדול של בוץ אי אפשר לשנות אותו או להוסיף פיצ'רים חדשים.
היום בצוותים שמערבים AI המוצר יכול להיתקע כבר אחרי כמה שבועות או חודשים, ואנשים מרגישים הרבה יותר בנוח לזרוק את הקוד ולהתחיל פרויקט חדש. זה קצת כמו תרמית פירמידה בה אנחנו מסבכים את הדורות הבאים של מתכנתים (שאגב יהיו פחות מוכשרים מאיתנו כי ה AI כתב להם את כל הקוד בלימודים).
הרבה אנשים חושבים שעוד מעט ה AI יגיע למצב שהוא כבר יוכל לתחזק גם את הקוד שהוא כותב והתפקיד של המפתחים האנושיים יהיה שונה משמעותית ממה שהוא היום. יכול להיות שזה יקרה ויכול להיות שלא, כרגע אנחנו עדיין לא שם.
הצגת הבעיות בפיתוח בעזרת AI לא באה כדי לבטל את שיטת העבודה או להוריד מערכה - המטרה היא להבין את המגבלות כדי לפתוח את הראש. אם AI מייצר תמונת מציאות חלקית, אני ארצה להיעזר בו כדי לקבל רעיונות ואז להרחיב על הרעיונות האלה דרך קריאה בגוגל וכך באיטרציות כדי לקבל תמונת מציאות שלמה יותר. אם AI גורם לי להיות פחות מיומן בפיתוח אני ארצה לחלק את הזמן שלי ולפחות לשעה ביום לכבות את ההשלמה האוטומטית כדי להתרגל לחשוב, או אולי לעבוד בלי השלמה אוטומטית בכלל ולגשת ל AI רק דרך לחיצת כפתור. ואם קוד שנוצר על ידי AI עלול ליצור חוב טכני אני ארצה לבנות את הארכיטקטורה לפני שאני מגיע לדבר עם ה AI ולדרוש שה AI יכתוב רק קוד שגם אני מבין והייתי יכול לכתוב לבד, וכך אם יהיה חוב טכני לפחות זה בגלל שטויות שלי.
1 417
פייתון מתוך רובי? אין בעיה
אין ספק שפייתון יותר פופולרית מרובי, כל מי שמוציא API חדש מוציא דוגמת קוד או ספריה לפייתון, ה AI יודע לעבוד יותר טוב עם פייתון וגם כל מה שקשור לניתוח מידע או למידת מכונה קיים בפייתון.
עכשיו מה עושים אם יש לכם מערכת רובי או אתר ריילס אבל אתם צריכים להפעיל קוד מהאקוסיסטם של פייתון? דרך אחת היא פשוט לקרוא ל python בתור תהליך חיצוני, וזה יעבוד אבל זה קצת מעייף להבין אם התהליך החיצוני של הפייתון סיים או התרסק באמצע או להעביר מידע בין שתי התוכניות. מנגנון יותר פשוט הוא הספריה pycall:
https://github.com/mrkn/pycall.rb
הוראות ההתקנה באתר שלהם עבדו לי ממש בסדר. תוכנית הדוגמה שלהם היתה קצת משעממת אז כתבתי סקריפט שמצייר פרה במקום:
require 'pycall/import'
include PyCall::Import
pyimport :cowsay
puts cowsay.cow('Hello World')
בשביל שזה יעבוד צריך להתקין את pycall, להתקין את פייתון ולהקפיד לקמפל אותו עם האפשרות להשתמש בו בתור ספריה עם הפקודה:
env CONFIGURE_OPTS="--enable-shared" pyenv install 3.13.3
וזה הכל. עכשיו תוכלו להנות מהאקוסיסטם של שתי השפות בתוכנית אחת.1 417
function moveBody(body: Array<Coordinates>, direction: Direction, grow: number) {
const head = body[0];
const newHead = {
'right': () => ({x: head.x + 1, y: head.y}),
'left': () => ({x: head.x - 1, y: head.y}),
'up': () => ({x: head.x, y: head.y - 1}),
'down': () => ({x: head.x, y: head.y + 1}),
}[direction]();
if (grow > 0) {
return [newHead, ...body]
} else {
return [newHead, ...body.slice(0, body.length - 1)]
}
}
קובץ קומפוננטה של נחש בשם snake.tsx:
import type { Snake, Coordinates } from "@/types/game";
import { coordinatesToStyle } from '@/lib/utils';
/**
* Renders the snake using DOM objects
* Each snake part should be a div with a class name of 'snake'
*/
export default function Snake({ snake }: { snake: Snake }) {
return (
<>
{snake.body.map((c, i) => (
<div
key={i}
className='w-5 h-5 bg-amber-900 absolute m-0 p-0'
style={coordinatesToStyle(c)}
/>
))}
</>
)
}
וקובץ משחק game.tsx שמייצר נחש, תפוח ו Game Loop ומתחיל להזיז דברים על המסך:
'use client';
import isColliding from "@/lib/is-colliding";
import useSnake from "@/hooks/use-snake";
import useGameLoop from "@/hooks/use-game-loop";
import useApple from "@/hooks/use-apple";
import Snake from "@/components/dom/snake";
import Apple from "@/components/dom/apple";
import PlayButton from "@/components/dom/play-button";
import { useState } from "react";
/**
* Create a Snake game client component
*/
export default function Game() {
const [isPlaying, setIsPlaying] = useState(false);
const snake = useSnake();
const apple = useApple();
const snakeEatsApple = isColliding(snake.body, apple.body);
const GAME_SPEED = 2; // move the snake 1 step every second
const gameLoop = useGameLoop(isPlaying ? GAME_SPEED : 0, () => {
snake.update();
});
if (snakeEatsApple) {
}
return (
<div>
<h1>Snake</h1>
<PlayButton isPlaying={isPlaying} toggle={() => setIsPlaying(p => !p)} />
<div className="border-purple-600 border w-[800px] h-[600px] relative mx-auto">
<Snake snake={snake} />
<Apple apple={apple} />
</div>
</div>
)
}
פה כבר היתה מספיק מסה של קוד כדי שה AI יבין איך המשחק צריך להיות בנוי ותוך רגע קיבלתי משחק סנייק שעובד. ה AI הוסיף את כל המנגנונים:
1. נחש יכול לאכול תפוח ולגדול.
2. שליטה בנחש עם כפתורי החצים או w, s, a, d.
3. כשנחש אוכל תפוח מקבלים נקודות, ומהירות המשחק לאט לאט עולה.
4. פסילה כשנחש נוגע בעצמו או בקירות העולם.
אפשר לראות את כל הקוד שקלוד כתב בצורת diff בקישור הזה:
https://github.com/ynonp/vibe-coding-snake-game/commit/7476986d0f7cc35ab70aa670c349dfdf5a4c531e
בעבודה עם AI יש נקודה שהחל ממנה ה AI כבר מסתדר להמשיך לבד וכותב קוד שמתאים לסגנון שאתם רוצים, אבל בשביל ששיתוף הפעולה הזה יעבוד טוב אתם צריכים להיות אלה שמבינים איך המערכת בנויה ומה התפקיד של כל קובץ וכל שורת קוד.
אחד הפרמטרים שמעניין להסתכל עליהם בניסיונות מהסוג הזה הוא הפרדיקטביליות של הקוד, כלומר אנחנו נותנים לקרסר ליצור את הקוד ואז שמים אותו ב branch, ואז נותנים לו ליצור שוב עוד גירסה עם אותו פרומפט, וככה ממשיכים 3-4 פעמים. ככל שנראה יותר שינויים בין הגירסאות שנוצרו זו אינדיקציה שהבסיס שלנו עדיין לא מספיק ברור והאלמנט האקראי בעבודה של ה AI משחק תפקיד יותר משמעותי.1 417
const gameLoop = useGameLoop();
if (areColliding) {
}
return (
<div>
<h1>Snake</h1>
<Snake snake={snake} />
<Apple apple={apple} />
</div>
)
}
שלושה קבצים ריקים עבור הלוגיקה שרציתי שתישמר בתור hooks עם השמות use-apple.ts, use-collision-detection.ts, use-game-loop.ts ו use-snake.ts. יצרתי גם קובץ טיפוסים בשם types/game.ts עם התוכן הבא:
export type GameObject = Coordinates | Array<Coordinates>;
export type Snake = {
body: Array<Coordinates>;
direction: Direction;
};
export type Coordinates = {
x: number;
y: number;
};
export type Direction = 'up' | 'down' | 'left' | 'right';
export type GameState = {
snake: Snake;
food: Coordinates;
gameOver: boolean;
};
export type GameConfig = {
width: number;
height: number;
gameOver: boolean;
speed: number;
}
יותר מזה, רציתי לוודא שבלולאת המשחק ה UI ישתמש ב requestAnimationFrame אז כתבתי בקובץ use-game-loop.ts את התוכן הבא:
import { useEffect } from "react";
export default function useGameLoop({
update,
isRunning
}: {
update: () => void;
isRunning: boolean;
}) {
let animationFrame: number;
function tick() {
if (isRunning) {
update();
animationFrame = requestAnimationFrame(tick);
}
}
useEffect(() => {
tick();
return () => cancelAnimationFrame(animationFrame);
}, [isRunning]);
}
למרות שלי היה ברור לגמרי איך להמשיך את המשחק, התוצאה שקיבלתי מה AI לא היתה טובה. כן זה היה יותר טוב מהסיבוב הראשון: הקבצים היו קטנים יותר, היתה התחלה של בדיקות, החלוקה לקבצים היתה לפי מה שאני הגדרתי והוא לא ניסה לממש את המשחק בערבוב מוזר של Canvas ו State. אבל, המשחק עדיין כלל באגים, הבדיקות לא היו טובות, ובאופן כללי המימוש לא איפשר הרחבה של המשחק בלי לשבור דברים.
ניסיון שלישי - מבנה בסיסי עובד, AI משלים
אחרי שנתתי למספר מודלים ליצור משחק סנייק לפי ההתחלה של הבסיס שכתבתי בחלק הקודם הבנתי שהבעיה לא במודלים אלא בפרומפט וחזרתי לשולחן השרטוטים. הפעם המשכתי את המשחק בעצמי עד שהגעתי ל Game Loop שמזיזה נחש. הקוד כלל את הקובץ use-game-loop.ts השלם:
import type { RefObject } from 'react';
import { useRef, useEffect } from "react";
function gameLoop(
nextAnimationFrameRef: RefObject<number>,
lastTime: number,
accumulatorRef: RefObject<number>,
updatesPerSecond: number,
update: () => void,
) {
const timeStep = 1 / updatesPerSecond;
const currentTime = performance.now();
let deltaTime = (currentTime - lastTime) / 1000;
accumulatorRef.current += deltaTime;
while (accumulatorRef.current >= timeStep) {
update();
accumulatorRef.current -= timeStep;
}
nextAnimationFrameRef.current = requestAnimationFrame(() => gameLoop(nextAnimationFrameRef, currentTime, accumulatorRef, updatesPerSecond, update));
}
export default function useGameLoop(
updatesPerSecond: number,
update: () => void,
) {
let accumulatorRef = useRef(0);
const nextAnimationFrameRef = useRef(0);
useEffect(() => {
if (updatesPerSecond > 0) {
gameLoop(nextAnimationFrameRef, performance.now(), accumulatorRef, updatesPerSecond, update)
return () => {
cancelAnimationFrame(nextAnimationFrameRef.current);
}
}
}, [updatesPerSecond, update])
}
קובץ use-snake עם הרבה יותר תוכן:
import { Coordinates, Direction, Snake } from "@/types/game";
import { useState } from "react";
/**
* Creates a new snake object
* Bind keyboard events to move the snake
*/
export default function useSnake(): Snake {
const [body, setBody] = useState([{x: 20, y: 20}]);
const [direction, setDirection] = useState<Direction>('right');
const [grow, setGrow] = useState(2);
return {
body: body,
direction,
update: () => {
setBody(body => moveBody(body, direction, grow))
if (grow > 0) {
setGrow(g => g - 1);
}
},
grow: (addition: number) => {
setGrow(g => g + addition)
}
}
}
现已上线!2025 年 Telegram 研究 — 年度关键洞察 
