Files
legal-ai/docs/spec/X16-pipeline-durability.md
Chaim cb822c4900 docs(spec): X15 שער-הפלטפורמה (G12) + X16 עמידות-פייפליין
X15 — Agent Platform Port: Paperclip כמעטפת ניתנת-להחלפה מאחורי Port יחיד.
מגדיר INV-PORT1/G12 (Ports&Adapters + Dependency Rule + Anti-Corruption Layer),
מצאי-דליפה baseline (mcp-server נקי; דליפה ב-app.py + 10 פרומפטים + web-ui),
מפת-תיקון R0–R4, ומנגנון-אכיפה נגד דליפה-עתידית (leak-guard + תבנית-PR).

X16 — Durable Pipeline Execution: LangGraph כספרייה בתוך הסקריפט (לא תחליף-פלטפורמה)
ל-final_halacha/final_learning. מגדיר INV-DUR1 (checkpointing+replay, מימוש משותף),
SqliteSaver תחת data/checkpoints, גרעיניות מדורגת P0–P3, שימור-חוזה-CLI.

מיישם/מחזק: G2 (X15), G3 (X16). תכנון בלבד — ללא שינוי-קוד.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:26:57 +00:00

7.2 KiB
Raw Blame History

X16 — עמידות-פייפליין (Durable Pipeline Execution)

כפוף ל-00-constitution.md. מחזק את INV-G3 (idempotency) ב-checkpointing+replay לפייפליינים הדטרמיניסטיים המקומיים. נושק ל-07-learning.md ו-X11-citation-corroboration.md.

0. הבעיה

שני הפייפליינים המקומיים החד-פעמיים — final_halacha_pipeline.py (כפתור run-halacha, אימות-הלכות, X11) ו-final_learning_pipeline.py (כפתור run-learning, למידת-סגנון, 07-learning) — חולקים צורה זהה: סקריפט מקומי, 34 שלבים בטור, idempotent, פאנל-LLM ארוך בסוף (CSV-gated, "can take minutes").

היום הם ליניאריים וחסרי-זיכרון: קריסה באמצע (ניתוק ל-DeepSeek/Gemini, restart של קונטיינר, OOM) → הרצה-מחדש מ-שלב 0. השלבים idempotent ולכן זה בטוח, אבל משלמים שוב: מחלצים, בונים corroboration על כל הקורפוס, ושופטים מחדש הלכות שכבר נשפטו — דקות וקריאות-LLM לפח.

הקשר-סיכון אמיתי: דליפת task-master (יתומים ppid=1, ~3GB) מסכנת OOM ל-Postgres ([project_taskmaster_mcp_memory_leak]). אם OOM הורג ריצת-פאנל ארוכה — היום מתחילים מאפס.

הבחנה מ-idempotency: idempotency = "בטוח להריץ שוב". durable execution = "בטוח להריץ שוב בלי לשלם שוב". זה שכלול, לא תחליף.

1. ההכרעה

להטמיע LangGraph כספרייה בתוך הסקריפט (לא כפלטפורמה מחליפה ל-Paperclip): מנוע-העמידות היחיד שהוא state-of-the-art ב-checkpointing+replay+time-travel, בשימוש כ-import בתוך הסקריפט המקומי. Paperclip לא מושפע — הכפתור עדיין מעיר את Hermes שמריץ את אותו ה-CLI.

גבול-תחום מפורש (מתחבר ל-G12/X15): LangGraph נכנס רק כמנוע-פנימי של הסקריפטים המקומיים. אסור להשתמש בו כתחליף-פלטפורמה או כ-orchestrator של הסוכנים — זה ייצור מסלול מקביל ל-Paperclip (הפרת G2) ויערבב עמידות עם פלטפורמה. HITL/ניתוב-יו"ר נשאר מאחורי ה-Port (ראו §4 Phase 3).

מקורות: Temporal — Durable Execution · Saga / workflow-checkpointing pattern · Martin Kleppmann, DDIA (idempotence & exactly-once) · LangGraph checkpointer/replay docs.

2. ה-invariant

INV-DUR1 — עמידות לפייפליינים דטרמיניסטיים

כלל: פייפליין דטרמיניסטי רב-שלבי משמר את התקדמותו ב-checkpoint מתמיד אחרי כל שלב שהושלם; הרצה-חוזרת של אותה יחידת-עבודה מדלגת על שלבים שכבר הושלמו ומתחילה מנקודת-הכשל המדויקת. מימוש-העמידות הוא משותף לכל הפייפליינים (scripts/_pipeline_runtime.py) — לא מימוש-לכל-סקריפט (G2). חוזה-הכניסה (ה-CLI) נשמר ללא-שינוי. מקורות: Temporal (Durable Execution) · Kleppmann DDIA (exactly-once) · Saga pattern (workflow checkpointing) | סטטוס: verified אכיפה: _pipeline_runtime.py עם LangGraph + checkpointer; thread_id דטרמיניסטי לכל יחידת-עבודה (תיק); בדיקת kill-and-resume שמאמתת ששלבים שהושלמו אינם רצים-מחדש. הפרה ידועה: היום final_halacha_pipeline.py / final_learning_pipeline.py ליניאריים — קריסה = הרצה-מחדש מלאה (חוזרים על extract/corroboration/panel).

3. ארכיטקטורה

scripts/_pipeline_runtime.py   ← מודול-עמידות משותף יחיד (G2)
  • build_graph(steps)          StateGraph: node לכל שלב
  • SqliteSaver                 data/checkpoints/<pipeline>.sqlite  (לא Postgres המשותף)
  • run(thread_id, resume)      מדלג-אוטומטית על nodes ב-checkpoint

הכרעות-תכנון:

  1. Checkpointer = SQLite (langgraph-checkpoint-sqlite), לא Postgres. קובץ תחת data/checkpoints/: מקומי (תואם "local-only"), פשוט, ונמנע מהאזהרה ב-CLAUDE.md נגד migrations מ-2 worktrees על Postgres המשותף (localhost:5433). PostgresSaver = אופציה עתידית אם נדרש ריכוז/observability.
  2. thread_id = f"<pipeline>:{case_number}". הרצה-חוזרת של אותו תיק מזהה checkpoint לא-גמור וממשיכה אוטומטית; תיק שהושלם = no-op. idempotency + דילוג-checkpoint מתחברים.
  3. גרעיניות (מדורגת):
    • גס (P0/P1): כל שלב = node. קריסה בין-שלבים → המשך מהשלב שנפל. הפאנל node יחיד שרץ-מחדש — אך הוא כבר CSV-backed + idempotent (מדלג פנימית על מה שנשפט).
    • עדין (P2, אופציונלי): פירוק הפאנל ל-map מעל ההלכות/הלקחים (LangGraph Send), כל פריט = יחידת-checkpoint → resume תוך-פאנל בלי לשפוט מחדש ברמת-LLM. נשען על ה-CSV הקיים כמקור "כבר-נשפט".
  4. סמנטיקת-כשל מפורשת. היום הכל "non-fatal, continue". עם LangGraph: nodes "מייעצים" (extract, corroboration) — catch+record-status וממשיכים; node "קריטי" (panel) — raise בכשל-קשה → עצירה ב-checkpoint → resume.
  5. שימור-חוזה-הכניסה. ה-CLI (--case/--limit/--dry-run) זהה; run-halacha/run-learning → Hermes → אותו python ...pipeline.py --case X לא משתנה. מוסיפים --fresh (ברירת-מחדל: auto-resume אם יש checkpoint לא-גמור לתיק).

4. גלגול מדורג

Phase תחום מאמץ
P0 deps ל-mcp-server/pyproject (langgraph + langgraph-checkpoint-sqlite, venv מקומי בלבד → אפס השפעת-קונטיינר). _pipeline_runtime.py עם SqliteSaver. עטיפת 4 שלבי-halacha כ-nodes (גס). CLI זהה. test: kill אחרי [1] → resume → assert [0],[1] לא רצו שוב ~1 יום
P1 אותו runtime על final_learning_pipeline (3 שלבים) — מימוש-עמידות אחד לשניהם (G2) חצי יום
P2 (אופציונלי) פירוק-פאנל ל-map per-item — resume תוך-פאנל 12 ימים
P3 (עתידי) LangGraph interrupt() ל-HITL של היו"ר (split→chair, INV-G10) — רק מאחורי ה-Port (X15/G12)

5. ראו גם