מדריך למפתחים מזהיר כי השארת טרנזקציה של מסד נתונים פתוחה למשך כל זמן הצ'אט מבוסס ה-AI עלולה להוביל לתשובות שגויות ולחנק של ה-DBMS שבבסיס המערכת. ההערה, המיועדת לצוותים המפתחים כלים מבוססי LLM, קובעת כי "אין לנקוט בפרקטיקה זו" ומציעה במקום זאת ארבע תבניות עקביות (consistency patterns) קצרות מועד.

למה האזהרה הזו חשובה

עוזרים מבוססי LLM שואלים לעיתים קרובות סדרה של שאלות המשך: הם קוראים רשומה, מבקשים פרט מסוים, ואז מבקשים סיכום (total). אם הנתונים שבבסיס משתנים בין השלבים הללו, העוזר עלול להחזיר נתונים סותרים – תשובה אחת תהיה שגויה. הפתרון המפתה הוא לפתוח טרנזקציה אחת בתחילת השיחה ולהשאיר אותה פעילה עד לסיום הצ'אט. בפועל, גישה זו תופסת גרסאות שורות (row versions), ממלאת את ה-tempdb, מחזיקה נעילות (locks) ומפריעה לניהול מאגר החיבורים (connection pooling).

מה מוביל לטרנזקציות ארוכות טווח

  • Multi-turn prompting – מודלי LLM מייצרים בדרך כלל מספר הנחיות (prompts) לפני שהמשתמש רואה תגובה.
  • קריאות לכלים (Tool calls) הפוגעות במסד הנתונים – כל שלב בשיחה עשוי להפעיל stored procedure, פקודת SELECT או UPDATE.
  • טווח טרנזקציה לא מבוקר – מפתחים עוטפים לעיתים את כל הצ'אט בבלוק של BEGIN…COMMIT, מתוך הנחה שזה מבטיח עקביות (consistency).

כאשר הצ'אט מתארך, מנוע מסד הנתונים חייב לשמור את גרסאות השורות המקוריות כדי שהטרנזקציה תראה תצוגה יציבה. הגרסאות הללו נשמרות ב-tempdb, מה שצורך שטח אחסון ומשאבי I/O. נעילות שמוחזקות לאורך אותה תקופה חוסמות כותבים מקבילים, והחיבור הלא פעיל (idle connection) עלול לרוקן את המאגר, מה שמאלץ פותחים חדשים להמתין למקום פנוי.

ארבע תבניות קצרות מועד

המדריך ממליץ להתייחס לעקביות כעניין של כל קריאת כלי (per-tool-call) ולא כעניין של כל שיחה. ארבע התבניות הן:

  1. Live statements – כל קריאה רצה תחת רמת הבידוד (isolation level) ברירת המחדל, ורואה רק נתונים שבוצע להם commit ברגע הביצוע. זהו המודל הפשוט ביותר; הקורא מקבל את העובדה שהנתונים עשויים להשתנות מאז השלב הקודם.
  2. Bounded transactions – מפתח מקבץ מספר פקודות בתוך טרנזקציה קצרה אחת שמסתיימת לפני שלב ה-LLM הבא. זה מבטיח אטומיות (atomicity) עבור אותה אצווה (batch) מבלי להישאר פעילה מעבר לקריאת הכלי.
  3. Snapshot reads – הפעולה מתחילה עם חותמת זמן (timestamp) מוגדרת של snapshot, מה שמעניק תצוגה יציבה של מסד הנתונים למשך זמן הקריאה. כל הקריאות בתוך הקריאה רואות את אותם נתונים, גם אם מתבצעות כתיבות מקבילות.
  4. Materialized reports – הכלי קורא מתוך סט תוצאות (result set) מוגדר מראש ובעל גרסה, המשקף את מסד הנתונים בנקודת זמן ידועה. לאחר מכן, דפדוף (pagination) או חישובים נוספים מתבצעים על סט הנתונים הקפוא הזה.

ב-SQL Server, בדקו אם READ_COMMITTED_SNAPSHOT פעיל. אל תניחו שהשם מספר את כל הסיפור.

כללים מעשיים לאפליקציות מבוססות LLM

  • קבצו את מה שאתם צריכים (Batching) – אם שאלה דורשת ערכים רבים, חשבו אותם בקריאת כלי אחת במקום להוציא שאילתות נפרדות שכל אחת מהן מתחילה טרנזקציה חדשה.
  • דפדוף דטרמיניסטי (Deterministic pagination) – בעת הצגת תוצאות על פני דפים, השתמשו במפתח מיון יציב, cursor, או ב-materialized result set. לעולם אל תשאירו טרנזקציה פתוחה בזמן שהמשתמש גולל.
  • החזירו ראיות (Evidence) – לצד הנתונים, כללו מטא-דאטה שהופך את מודל העקביות למפורש: מחלקת העקביות (consistency class), זמן תחילת ה-snapshot, נקודת חיתוך הדיווח, רעננות הנתונים, מספר השורות, זהות מסד הנתונים ו-trace ID.
  • בצעו בדיקות עומס עם מקביליות (Concurrency) – סמלו כתיבות מקבילות בזמן שה-LLM מייצר הנחיות, וודאו שהאפליקציה מבצעת ניסיונות חוזרים (retries) או נכשלת בצורה אלגנטית (gracefully).

השורה התחתונה ברורה: צ'אט AI לא אמור להכתיב את אורך החיים של טרנזקציית מסד נתונים. על ידי הגדרת טווח העקביות לכל קריאת כלי, מפתחים שומרים על בריאות מסד הנתונים, שומרים על ביצועים לכל המשתמשים, ומספקים ל-LLM מספיק נתונים אמינים כדי לענות במדויק.