Cairn: לקמפל ידע, לא לאחזר אותו
RAG מגלה מחדש את הידע שלך בכל query. Cairn מקמפל אותו פעם אחת, בזמן ה-ingest — agent עם router tool, בנוי על ה-LLM-wiki pattern של Karpathy, ועם שלב האימות שגורם לזה להחזיק.
בניתי agent שעונה על שאלות על כל הארכיון של יוצר תוכן — podcasts, מאמרים, קטלוג של כתיבה. Claude בחר את השם: Cairn, על שם תלי האבנים שמסמנים שביל ונבנים לאורך זמן בידיים רבות. הוא בחר אותו בגלל מטאפורת ההצטברות, ואני השארתי אותו — זה בדיוק מה שבסיס הידע עושה: כל מקור מוסיף אבן, התל מצטבר, והוא מסמן את הדרך למי שבא אחריו.
השם עושה עבודה אמיתית, כי הדבר שמבדיל את Cairn ממערכת RAG רגילה הוא בדיוק ההצטברות הזו. אז זה ה-pattern, ההחלטה האחת שמגדירה אותו, והלקח שהפתיע אותי כשבניתי אותו.
ההחלטה האחת: מתי עושים את הסינתזה?
מענה על שאלות על גוף תוכן דורש סינתזה — להחליט מה חשוב, לקשר רעיונות בין מקורות שונים, לשים לב היכן שני מקורות סותרים זה את זה. מישהו, או משהו, צריך לעשות את העבודה הזו. השאלה האמיתית היחידה היא מתי.
מערכת RAG רגילה עושה את זה בזמן השאלה. בכל פעם שמשתמש שואל משהו, היא מבצעת embedding לשאלה, מחפשת ב-vector index, מושכת בחזרה את ה-chunks הכי דומים של טקסט גולמי, ומגישה למודל ערימה טרייה להסיק ממנה. הסינתזה קורית מאפס, בתוך אותה קריאת answer אחת, ואז נזרקת. שואלים את אותו ארכיון אלף שאלות דומות והוא עושה את אותו גילוי מחדש אלף פעמים. שום דבר לא מצטבר.
Cairn עושה את זה בזמן ה-ingest — ברגע שמקור חדש נוסף, הרבה לפני שמישהו שואל שאלה. transcript חדש של podcast מגיע, וה-agent קורא אותו, כותב סיכום מובנה, מעדכן את ה-index, ומטייל בדפים הקשורים כדי לתקן אותם. עד שהשאלה באמת מגיעה, הסינתזה כבר עשויה וכתובה. השאלה זולה כי החלק היקר כבר קרה, פעם אחת.
מי שכתב תוכנה כבר מכיר את ה-trade-off הזה בשם אחר. זה ההבדל בין interpreter ל-compiler. interpreter מפענח מחדש את המקור בכל הרצה; compiler עושה את העבודה הזו פעם אחת ומייצר artifact שאפשר להריץ בזול לנצח. RAG הוא ה-interpreter. Cairn הוא ה-compiler — הוא מקמפל את הארכיון לבסיס ידע חי ומובנה, ושומר אותו עדכני ככל שמקורות חדשים נוחתים.
מאיפה ה-pattern מגיע
הצורה של זה היא ה-LLM-wiki gist של Andrej Karpathy. הרעיון: במקום לאחזר chunks בזמן ה-query, ה-LLM בונה ומתחזק באופן הדרגתי wiki — קבצי markdown מובנים ומקושרים שיושבים בינך לבין המקורות הגולמיים. יש לו שלוש שכבות:
- מקורות גולמיים למטה, immutable — ה-LLM קורא אותם אבל לא עורך אותם.
- ה-wiki באמצע — סיכומים, דפי קונספטים, הפניות צולבות — בבעלות מלאה של ה-LLM.
- מסמך schema למעלה, שמגדיר איך ה-wiki מבונה ומתוחזק.
ingest של מקור הוא לא “להוסיף שורה ל-index”. הוא: לקרוא את המקור, לכתוב את דף הסיכום שלו, לעדכן את ה-index, לתקן את דפי הקונספטים הקשורים, לתעד את ה-ingest. מקור אחד נוגע בעשרה עד חמישה-עשר דפים. זה נשמע יקר עד שנזכרים שהאלטרנטיבה היא לשלם גרסה קטנה יותר של העלות הזו בכל שאלה בודדת, לנצח, ולא לשמור כלום.
איך זה נראה ב-production
Cairn רץ על ארכיון מלא של יוצר — חמש יצירות כתובות באורך מלא ועוד בערך שמונים פרקי podcast.
צד ה-query הוא agent עם router tool. נותנים ל-router שאלה, הוא מחפש ב-index ומחזיר את קומץ המקורות שהכי סביר שמחזיקים את התשובה. ה-agent מחליט אם בכלל לקרוא לו — בשאלת המשך בשיחה, כשהמקורות הרלוונטיים כבר ביד, הוא מדלג על ה-routing ועונה ישירות. החיפוש רץ בתוך ה-tool, כך שה-index המלא אף פעם לא צריך לשבת ב-context של ה-agent. ברגע שיש ל-agent את המקורות הנכונים, הוא טוען אותם וכותב את התשובה בעצמו, מצטט ישירות עם מקורות — הארכיון המלא אף פעם לא בתוך prompt אחד.
לצד האחסון יש שתי צורות, נבחרות לפי סוג התוכן:
- כתיבה מחברתית — המילים והטרמינולוגיה של המחבר עצמו הן העיקר, אז אין שכבת פרפרזה; ה-agent מצטט מקורות גולמיים ישירות. Cairn עדיין מתמודד עם שאלות פתוחות, מסוג דיון, לא רק חיפושים ישירים — אבל כל דבר מהותי בתשובה נשאר ציטוט ישיר עם מקור, אף פעם לא ערבוב ללא ייחוס.
- חומר מתומלל — פרפרזה היא בסדר, אז Cairn משתמש במבנה המלא של שלוש השכבות מה-pattern של Karpathy: ה-transcripts הגולמיים למטה, ה-wiki של סיכומים והפניות צולבות שמתוחזק על ידי ה-LLM מעליהם, וה-schema שמגדיר איך ה-wiki מאורגן.
Agent מול pipeline
גרסה קודמת של Cairn היתה pipeline קבוע: קריאת router אחת, אז קריאת answer אחת, כל פעם. מה שרץ עכשיו הוא agent, וההבדל הוא לא שה-agent עושה יותר — הוא עושה פחות. pipeline מנתב בכל שאלה בין אם צריך ובין אם לא. agent קורא את השאלה קודם ומחליט: כשכבר יש לו את מה שהוא צריך — שאלת המשך בשיחה, המקורות עוד ביד — הוא מדלג על ה-router ועונה ישירות. ההחלטה הזו היא מה שהופך אותו ל-agent ולא ל-script.
ה-catch הוא שהחלטה יכולה להיות שגויה. ברגע שה-agent יכול לבחור לא לנתב, הוא יכול לבחור לא לנתב כשהיה צריך — ולענות מ-context ישן, או מהזיכרון של המודל עצמו, במקום ללכת לבדוק. pipeline קבוע לא יכול לעשות את הטעות הזו: הוא בזבזני, אבל הוא בטוח. agent מחליף את הביטחון הזה בשיקול דעת.
אז שיקול הדעת חייב להיות ניתן לבדיקה. כל החלטה שה-agent מקבל נכתבת ל-database — מה הוא נשאל, אם ניתב, מה ה-router החזיר, מה הוא ענה. ה-log הזה הוא ההבדל בין לסמוך על שיקול הדעת של ה-agent לבין היכולת לבדוק אותו. agent שאי אפשר לבקר הוא סתם pipeline עם failure modes גרועים יותר.
הלקח שהפתיע אותי: לאמת את ה-compiler
הנה הדבר שלא ציפיתי לו.
ה-compiler מהזה. כשנותנים ל-LLM לבנות routing index — לעבור על התוכן ולרשום, לכל ערך, את הקונספטים והמונחים שמופיעים בו — בערך רבע ממה שהוא מציע שגוי. לא שגוי בעדינות: או מונח שזלג מערך שכן שהמודל גם מכיר, או פרפרזה סבירה-למראה שאף פעם לא מופיעה מילולית במקור. אם משאירים את זה ב-index, זה שובר את המערכת באופן אקטיבי. ה-router מתאים את המונח, בוחר את הערך הזה, קריאת ה-answer טוענת את התוכן שמאחוריו, המונח לא שם, והמשתמש מקבל פרוזה בטוחה בעצמה בלי שום דבר מאחוריה.
התיקון מכני ומשעמם. לכל מונח שה-LLM הציע, עושים grep לתוכן שהערך מצביע אליו. אם המונח לא שם מילולית, מורידים אותו. בבנייה אחת של index, 1038 מונחים מוצעים ירדו ל-791 אחרי הגיזום — 247 נפלו. לולאת bash חיסלה סוג שלם של כשל בתוך אחר צהריים.
זה מתכלל הרבה מעבר ל-routing indexes. בכל מקום שבו LLM כותב מידע מובנה ש-LLM אחר אז סומך עליו — ארגומנטים של function-call שחולצו, תגיות שהוקצו, סיווגים שמניעים routing — בדרך כלל יש בדיקה דטרמיניסטית זולה זמינה: grep, regex, ולידציה של schema, בדיקת קיום. היא מהירה, היא טיפשה, והיא מצטברת. זה שה-compiler שוטף ומשכנע הוא כל העניין, וכל הסיכון. אימות הוא מה שמאפשר לדחוף את ה-compiler באגרסיביות ועדיין לסמוך על מה שיוצא.
הכיוון נכון
אחרי שהרצתי את Cairn בערך חודש, Pinecone הכריזה על מוצר חדש שבנוי סביב מה שהם קוראים לו “context compiler” — לבנות את הידע מלמעלה ל-artifacts מוכנים במקום לאחזר מסמכים גולמיים בזמן ה-query. אותו רעיון ליבה, שהגיעו אליו באופן עצמאי, מחברה שכל העסק שלה הוא צד האחזור. כששני צדדים מושיטים יד לאותה מילה — compiler — בלי תיאום, זה בדרך כלל סימן שהכיוון נכון.
זו גם תזכורת שלא בהכרח צריך את התשתית הכבדה כדי להגיע לשם. Cairn הוא קבצי markdown ו-LLM שלא משתעמם מלעדכן הפניות צולבות. בקנה המידה שבו רוב הבעיות האלה באמת חיות — ארכיון של יוצר אחד, לא data warehouse של ארגון שלם — זה כל ה-stack. הסינתזה נפתרת פעם אחת ונשמרת, במקום להיגזר מחדש בכל שאלה. הידע מצטבר. כל מקור מוסיף אבן.
לקמפל את הידע שלך. ואז לאמת את ה-compiler.
מבנה שלוש השכבות כאן הוא ה-LLM-wiki pattern של Andrej Karpathy. גרסת ה-routing בשתי שכבות לכתיבה מחברתית, ושלב האימות המכני, הם תוספות מהרצה ב-production.