ספרחוב: משמרת לילה ל־Claude Code, ומצלמה לידיים רועדות
בזמן שאני ישן, Claude Code עובד. כל לילה ב־23:30 הוא מתעורר על השרת שבבית, לוקח את הניסוי הבא מהרשימה, מריץ אותו על הכרטיס הגרפי, ובבוקר מחכה לי מייל עם התוצאות. הניסוי הראשון מצא 59 מתוך 68 ספרים על מדף, במקום 47. והחלק המעניין באמת: איך נותנים לבינה מלאכותית לעבוד לבד כל הלילה, בלי לחשוש שתשבור משהו, ובלי שתגמור לי את המכסה של הבוקר.
לפני ארבעה ימים כתבתי את ספרחוב, עשרה ימים אחרי: האפליקציה שקוראת מדפים בספריות רחוב בעזרת מודלים של בינה מלאכותית על כרטיס גרפי אחד משנת 2016 התאימה לספרים רשומת קטלוג נכונה ב־85% מהמקרים, לעומת 45% קודם.
הפוסט ההוא עסק בקריאת הספרים שהאפליקציה מוצאת. הפוסט הזה עוסק בעיקר בספרים שהיא לא מוצאת בכלל, ובאנשים שמחזיקים את הטלפון. שלושה דברים השתנו:
- ל־Claude Code יש משמרת לילה. הוא מריץ ניסויים על השרת שבבית בזמן שאני ישן, על מנוי Claude שלי, כמשתמש שיכול לקרוא את הנתונים אבל לא יכול לשנות שום דבר חשוב.
- הניסוי הראשון מצא הרבה יותר ספרים: 59 מתוך 68 על מדפי הבדיקה, לעומת 47 היום. הוא עוד לא באוויר, כי הוא מכפיל את הזמן לכל תמונה.
- המצלמה הפסיקה לחכות ליד יציבה. היומנים של הטלפון עצמו הראו למה קוראים רגילים קיבלו כל כך מעט תמונות, וזה לא היה מה שחשבתי.
באפליקציה עצמה נכנסו מאז 20 שינויים. אלה שחשובים לקוראים מופיעים בסוף.
הספרים שאף אחד לא רואה
כשאני מאשר ספרים בתור, אני רואה רק את מה שהאפליקציה מצאה. ספר שהיא פספסה לא מגיע אליי בכלל, אז לא ידעתי כמה כאלה יש. אז שאלתי:
research ways to improve finding missing spines, maybe train a llm model with extra few layers to do this?
(בתרגום חופשי: תחקור איך למצוא שדרות שחסרות, אולי לאמן מודל שפה עם כמה שכבות נוספות?)
התשובה של Claude Code הייתה: קודם למדוד. הוא לקח 7 תמונות מדף שבהן כבר אישרתי כל ספר, 68 ספרים בסך הכול, ובדק איפה כל ספר שפוספס הלך לאיבוד:
- האם בכלל סומנה סביבו מסגרת של שדרה?
- האם סומנה מסגרת, אבל הקריאה הייתה שגויה?
- או שהוא פשוט נפל בין השורות?
רוב הספרים שפוספסו לא קיבלו מסגרת בכלל. מודל הראייה מסתכל על כל התמונה בבת אחת ומצייר מסגרת סביב כל שדרה. על מדף צפוף הוא מוותר אחרי השדרות הברורות. השדרות הדקות, הספרים השוכבים ואלה שבצל לא מקבלים מסגרת, ולכן אף שלב בהמשך השרשרת לא מסתכל עליהם.
זה שינה את התוכנית. אימון של שכבות נוספות על מודל השפה היה משפר בעיקר את הקריאה, וזו לא הייתה הבעיה העיקרית. הרעיונות הזולים באו קודם:
- קודם לגזור כל שדרה, ואז לקרוא אותה, במקום לקרוא קודם את כל התמונה. נמדד על 7 התמונות: אותם 47 ספרים, ו־100 דקות במקום 63. ירד מהפרק.
- להסתכל על התמונה ברצועות. שלוש רצועות אופקיות חופפות, כל אחת נשלחת למודל לבד, כך שכל שדרה גדולה יותר בתמונה. אחר כך מחברים את המסגרות, מאחדים את שני החצאים של ספר שנחתך בקצה רצועה, ומשליכים מסגרת שהיא בעצם מדף שלם.
הרצועות מצאו 59 מתוך 68 ספרים, לעומת 47. הנה תמונה אחת לפני ואחרי. ירוק הוא ספר שהאפליקציה מצאה, אדום הוא ספר שאני יודע שנמצא שם והאפליקציה פספסה:


בתמונה הזו זה עלה מ־5 ספרים ל־16 מתוך 17. הרצועות גם מצאו ספרים אמיתיים שמעולם לא הוצגו לי, כי הם פוספסו בפעם הראשונה: מילן קונדרה, פאולינה סיימונס, The Travel Diary of Amos Lee.
הבעיה היא המחיר. רצועות דורשות 980 קריאות למודל עבור 7 התמונות במקום 406, כלומר 147 דקות במקום 66. על GTX 1080 אחד זה חוצה בחצי את מספר התמונות שהאפליקציה יכולה לקרוא ביום. לכן זה עוד לא באוויר. הניסוי הבא: להשתמש ברצועות רק כשהמעבר הראשון נראה שגוי (מעט מסגרות, או מסגרת אחת רחבה בהרבה מהאחרות), ולבדוק אם כך נשמר רוב הרווח בשבריר מהמחיר.
כל הרצה כזו לוקחת שעה עד שעתיים של זמן כרטיס גרפי. בגלל זה רציתי שהן ירוצו בלילה.
משמרת לילה ל־Claude Code
שאלתי אם Claude Code יכול להריץ את הניסויים האלה לבד בלילה, עם טוקן המנוי שלי, כמו שפרויקט של לקוח שלי רץ על הטוקן של בעליו.
הוא יכול. claude setup-token נותן טוקן לטווח ארוך עבור המנוי, ו־claude -p מריץ משימה בלי אף אחד ליד המקלדת. בכל לילה ב־23:30 טיימר על השרת שבבית מפעיל את Claude Code עם הוראה אחת: לקחת את הפריט העליון מרשימת הניסויים, להריץ אותו על תמונות הבדיקה, לכתוב את התוצאה, ולעבור לפריט הבא אם נשאר זמן.
החלק הקשה לא היה להפעיל אותו. בלילה הוא רץ בלי בקשות אישור, כך שאף אחד לא מאשר כל פקודה. לכן הגבולות צריכים לבוא מהמכונה, לא מההוראות:
- משתמש Unix משלו,
lab. בלי sudo. לא בקבוצת Docker, שבלינוקס שווה כמעט להרשאות root. הוא לא יכול לקרוא את התיקייה שלי או את הסודות של האפליקציה. - מסד הנתונים לקריאה בלבד. תפקיד Postgres שיכול רק לקרוא את חמש הטבלאות שהוא צריך. בדקתי שכתיבה נדחית.
- התמונות והעקבות לקריאה בלבד. הן מחוברות לתיקייה שלו במצב קריאה בלבד.
- הסריקות של הקוראים קודמות על הכרטיס הגרפי. כל העבודה על הכרטיס הגרפי בשרת שלי עוברת דרך תור עדיפויות אחד. ביום ה־lab בעדיפות הנמוכה ביותר. בלילה הוא עולה לרמה של הקוראים: בעדיפות הנמוכה הוא התחלף בתורות עם זרם של עבודות קטנות של פרויקט אחר, והספיק קריאה אחת למודל כל חמש דקות.
- הוא לא יכול לשחרר שום דבר לאוויר. יש לו הרשאת כתיבה למאגר GitHub פרטי אחד: ההערות והתוצאות שלו. שינוי בספרחוב הולך לענף במאגר על אותו שרת, מאחורי הגדרה שכבויה כברירת מחדל. אני מושך אותו ומחליט.
- עצירות קשיחות. שבע שעות לכל היותר, 300 צעדים לכל היותר, 80% מהמעבד לכל היותר, וקובץ
STOPשעוצר את ההרצה מיד.
בבוקר אני מקבל מייל עם סיכום של שורה אחת וקישור לדף על השרת שלי: כל תמונת מדף, וכל ספר בה מסומן בירוק או באדום, כך שאני רואה את התוצאה במקום לקרוא מספרי זיהוי. שתי התמונות למעלה הן מהדף הזה.
לא לאכול את המכסה של הבוקר
הרצת לילה משתמשת באותו מנוי שאני עובד איתו ביום. במנויי Claude יש חלון של 5 שעות ומגבלה שבועית, ואני לא רוצה לשבת בבוקר ולגלות שרובוט גמר לי את החלון.
הטוקן של setup-token לא יכול לפתוח את דף השימוש, אז עבודת הלילה שולחת בקשה של מילה אחת (כ־25 טוקנים) וקוראת את השימוש מהתשובה. היא עושה את זה בהתחלה, כל 10 דקות, ובסוף. היא עוצרת מוקדם אם:
- חלון 5 השעות שעוד יהיה פתוח ב־08:00 מנוצל ביותר מ־25%;
- חלון 5 שעות כלשהו מנוצל ביותר מ־90%;
- הלילה הזה ניצל 10% מהשבוע;
- או שהשבוע מנוצל ביותר מ־80%.
הדוח נגמר בטבלה קטנה: חלון 5 השעות והשבוע בתחילת הלילה ובסופו, ומתי כל אחד מתאפס.
מצלמה לידיים רגילות
הבקשה השנייה הייתה על האנשים שמחזיקים את הטלפון:
Currently it is very sensitive and the user needs to support the hand holding the phone so it is perfectly still. We need to allow user who is not a perfect photographer to scan shelves quickly and easily.
(בתרגום חופשי: היא רגישה מדי, והמשתמש צריך לתמוך ביד שמחזיקה את הטלפון כדי שתהיה יציבה לגמרי. צריך לאפשר גם למי שאינו צלם מושלם לסרוק מדפים מהר ובקלות.)
המצלמה מצלמת לבד כשהתמונה חדשה והטלפון מפסיק לזוז. הנחתי שהפתרון הוא סף מקל יותר. היומנים של הטלפון אמרו משהו אחר. כל סשן מצלמה שולח הביתה דוח של מספרים: כמה מהר התמונה זזה, כמה זמן לקחה כל תמונה. ששת הסשנים האחרונים הראו את זה:
- קידוד כל תמונת 4K ל־JPEG לקח בטלפון כ־9 שניות.
- בינתיים בדיקת היציבות, שאמורה לרוץ 7 פעמים בשנייה, רצה פעם ב־1.4 שניות.
- ב־1.4 שניות יד זזה מספיק כדי שהבדיקה “תאבד” את התמונה בחצי מהפעמים, ותמונה שאבדה נספרה כתנועה מהירה.
- לכן השלט “לאט יותר” הופיע כ־40% מהזמן, והתמונות נלקחו 2 עד 6 שניות אחרי שהקורא עצר.
הטלפון לא היה רגיש מדי. הוא היה עסוק מדי כדי להסתכל.
השינויים:
- ה־JPEG נוצר עכשיו ברקע (worker), כך שהבדיקות ממשיכות לרוץ בזמן שתמונה נשמרת.
- רעד הוא לא תנועה. יד רועדת מזיזה את התמונה מהר אבל לא הולכת לשום מקום; הליכה לאורך מדף הולכת למקום כלשהו. “מחזיקים” פירושו עכשיו שהסחיפה נטו של התמונה בשנייה האחרונה קטנה, לא משנה כמה היא רועדת.
- היא לומדת את היד שלך. הרעד של כל קורא נמדד ונשמר בטלפון, והגבולות של “יציב” ו”יציב בערך” גדלים איתו.
- היא אף פעם לא מחכה לשלמות. לכל היותר 1.6 שניות אחרי שעצרת, היא לוקחת את הפריים הטוב ביותר שראתה: חד, ומרגע שבו הג’ירוסקופ אמר שהטלפון היה הכי רגוע.
- תקלה אחת היא לא תנועה. תמונה שאבדה בזמן שהג’ירוסקופ אומר שהטלפון היה שקט לא נספרת.
בדקתי את זה ב־Chrome עם מצלמה מדומה: וידאו של מדף עם ארבע עצירות, ואחרי כל אחת תנועה לאורך המדף.
| היד המדומה | המצלמה הישנה | המצלמה החדשה |
|---|---|---|
| רעד חזק | 0 תמונות ב־12 שניות | תמונה בכל אחת מ־4 העצירות |
| רעד קל | 4 מתוך 4 | 4 מתוך 4 |
ידיים יציבות לא מפסידות כלום, וידיים רועדות עוברות מכלום לכל עצירה. וידאו הבדיקה לא היה ב־4K, כך שהקידוד האיטי לא היה חלק ממנה. המבחן האמיתי הוא הסשנים הבאים בטלפונים אמיתיים, ודוחות המצלמה יראו אותו.
והשאר, בקצרה
- מצלמה אחת להכול. מדפים, כריכות קדמיות וכריכות אחוריות מצולמים בכל סדר, והשרת מבין מה זה מה ומצמיד חזיתות לגב. קודם הטלפון ניחש, וב־3 בנובמבר הוא פיצל 3 ספרים ל־6, כי ספר שהופכים על המדף זז בתמונה בדיוק כמו טלפון שממשיך הלאה.
- רמת הביטחון נבדקה מול ההחלטות שלי. כ־560 אישורים ודחיות מ־6 עד 10 בנובמבר. הסימן החזק ביותר היה האם רשומת הקטלוג היא של הסופר שמודפס על השדרה. עם הכללים החדשים, ספרים עם ציון 75 ומעלה מסכימים עם ההחלטה שלי ב־96% מהמקרים, לעומת 84%. כל 46 הספרים בדרגה העליונה החדשה, “בטוח”, אושרו. אפשר היה לאשר אותם בלעדיי, אבל המתג הזה נשאר כבוי עד שקבוצת ההחלטות הבאה תאשר את זה.
- הקטלוגים מקבלים יחס עדין. הבקשות לכל אתר קטלוג מרווחות, ואני מקבל מייל כשתור הסריקות מתארך.
- בדיקות. ב־11 בנובמבר שינוי השאיר שני מסכים תקועים על “טוען…” באתר החי. עכשיו כל דחיפה מריצה lint, בדיקות יחידה, בנייה, ובדיקה מקצה לקצה שפותחת כל מסך בדפדפן אמיתי, כמבקר, כקורא וכמנהל, בעברית ובאנגלית. דחיפה שנכשלת לא יוצאת. ההרצה הראשונה גם מצאה כפתור במצלמת הכריכות שהפסיק לעבוד.
מה למדתי
- למדוד איפה דברים הולכים לאיבוד לפני שמאמנים משהו. “לאמן מודל” היה הרעיון הראשון שלי. המדידה הראתה שהספרים הלכו לאיבוד עוד לפני שהקריאה התחילה, ושינוי זול בהרבה מצא את רובם.
- יומנים משימוש אמיתי מנצחים את הניחושים שלי. הייתי בטוח שהמצלמה קפדנית מדי. היא הייתה איטית מדי להסתכל, ורק המספרים של הטלפון עצמו הראו את זה.
- בינה מלאכותית בלי השגחה היא שאלה של הרשאות, לא של הוראות. עבודת הלילה בטוחה בגלל מה שהמשתמש
labלא יכול לעשות, לא בגלל מה שאמרתי ל־Claude Code לא לעשות.
כמו קודם, הכול נבנה עם Claude Code. החלק שלי היה להשתמש באפליקציה באמת, להחליט מה נחשב נכון, ולבקש מספרים לפני שסומכים על תיקון.
אם יש לכם ניסויים איטיים שיכולים לרוץ בזמן שאתם ישנים, על החומרה שלכם ועל המנוי שלכם, אותה שיטה עובדת גם מחוץ לעולם הספרים. צרו קשר ואראה לכם איך זה מחובר.