תשעת אלפים בדיקות עברו
סיימון ווילסון נתן ל AI לתרגם ספריית פענוח HTML מפייתון ל JavaScript. הקוד המקורי בפייתון נמצא כאן:
https://github.com/EmilStenstrom/justhtml/
והקוד של סיימון ב JavaScript נמצא כאן:
https://github.com/simonw/justjshtml/
לספריה יש חבילת בדיקות של 9,200 בדיקות וה AI נעזר בבדיקות אלה בזמן כתיבת הקוד. כל פעם ה AI כתב קוד, הריץ את הבדיקות, זיהה מה לא עבר ותיקן. התוצאה היא חבילה עם תאימות כמעט מלאה שנכתבה בזמן שסיימון קישט עץ חג מולד וצפה בסרט.
סיימון העלה שאלות חשובות בבלוג שלו לגבי המשמעויות של תרגום כזה על עולם התוכנה בכללותו ועל המשמעות והחשיבות של קוד בפרט. אני ממליץ לקרוא את הפוסט שלו גם. אני רוצה כאן להוסיף כמה תהיות משלי.
הקוד עצמו של ה AI של סיימון באמת מאוד דומה לקוד המקורי רוב הזמן, אבל מדי פעם הוא בורח, למשל ה AI מחק הערות בחלק מהקבצים, מחק מגבלות בקבצים אחרים ולפעמים אפילו שינה לוגיקה. דוגמה אחת היא הקלאס StreamSink שבגרסת הפייתון נראה כך:
class _DummyNode:
namespace: str = "html"
class StreamSink:
"""A sink that buffers tokens for the stream API."""
tokens: list[StreamEvent]
open_elements: list[_DummyNode]
def __init__(self) -> None:
self.tokens = []
self.open_elements = [] # Required by tokenizer for rawtext checks
def process_token(self, token: Tag | CommentToken | DoctypeToken | Any) -> int:
# Tokenizer reuses token objects, so we must copy data
if isinstance(token, Tag):
# Copy tag data
self.tokens.append(
(
"start" if token.kind == Tag.START else "end",
(token.name, token.attrs.copy()) if token.kind == Tag.START else token.name,
)
)
# Maintain open_elements stack for tokenizer's rawtext checks
if token.kind == Tag.START:
# We need a dummy object with namespace for tokenizer checks
# Tokenizer checks: stack[-1].namespace
# We can just use a simple object
self.open_elements.append(_DummyNode())
else: # Tag.END
if self.open_elements:
self.open_elements.pop()
# If open_elements is empty, we ignore the end tag for rawtext tracking purposes
# (it's an unmatched end tag at the root level)
elif isinstance(token, CommentToken):
self.tokens.append(("comment", token.data))
elif isinstance(token, DoctypeToken):
dt = token.doctype
self.tokens.append(("doctype", (dt.name, dt.public_id, dt.system_id)))
return 0 # TokenSinkResult.Continue
def process_characters(self, data: str) -> None:
"""Handle character data from tokenizer."""
self.tokens.append(("text", data))
ובגרסת ה JavaScript נראה כך:
class StreamSink {
constructor() {
this.events = [];
this.openElements = [{ namespace: "html" }];
}
processToken(token) {
if (token instanceof Tag) {
if (token.kind === Tag.START) {
this.events.push(["start", [token.name, { ...(token.attrs || {}) }]]);
} else {
this.events.push(["end", token.name]);
}
return TokenSinkResult.Continue;
}
if (token instanceof CommentToken) {
this.events.push(["comment", token.data]);
return TokenSinkResult.Continue;
}
if (token instanceof DoctypeToken) {
const dt = token.doctype;
this.events.push(["doctype", [dt?.name ?? null, dt?.publicId ?? null, dt?.systemId ?? null]]);
return TokenSinkResult.Continue;
}
return TokenSinkResult.Continue;
}
processCharacters(data) {
this.events.push(["text", data]);
}
}
גם בלי לקרוא לעומק אנחנו רואים שההערות נמחקו. ב JavaScript המידע עדיין מועתק אבל בלי ההסבר שהיה בהערה בפייתון. מי שרק מסתכל על השורה הזו ב JavaScript אולי לא מבין מה מטרתה:
this.events.push(["start", [token.name, { ...(token.attrs || {}) }]]);
} else {
יותר מזה גם את הלוגיקה של open_elements ה AI מחק לגמרי וכעת משתנה זה נשאר בתור משתנה של המחלקה בלי שימוש אמיתי שם.
אבל הבדיקות עברו...
אני שומע אתכם. סט של 9,200 בדיקות חייב לשכנע אותנו שהספריה תואמת "100%" מבחינת התנהגות לספריה המקורית. וזה בדיוק מה שהופך את הדוגמה הזאת למוצלחת כדי שנשים לב שקוד הוא לא רק התוצאה שלו. ההערות, ההכנה שאנחנו בונים פנימה לשינויים עתידיים, אפילו הבחירה בשמות מסוימים ובסגנון כתיבה מסוים, כל אלה משפיעים על איכות המערכת. משפיעים על היכולת לתחזק, לשפר ולהרחיב אותה מחר ובהמשך.
הבעיה כאן היא לא ש AI תרגם את הקוד. כמו ש AI תרגם את הקוד הוא היה יכול לתרגם גם את ההערות ולשמור על הרוח המקורית והקריאות של הספריה המקורית. הבעיה היא של AI אין "טעם", אין את החוויה האנושית של לקרוא קוד ולחשוב האם זה קוד הספריה שאני רוצה, האם הקוד הזה מבטא את הידיעות והניחושים שלי לגבי העתיד של הספריה.
הבעיה היא לא ש AI כתב את הקוד אלא שאף אחד לא קרא את הקוד והתלבט בכל השאלות שהטרידו את היוצר של ספריית הפייתון המקורית. הכל בסדר ש AI יכתוב קוד כל עוד יש בסוף בן אדם שקורא אותו.