Files
legal-ai/docs/spec/01-ingest.md
2026-05-30 14:46:23 +00:00

13 KiB
Raw Permalink Blame History

01 — קליטה מאוחדת (Unified Ingest Contract)

קובץ-תחום זה כפוף ל-חוקת המערכת ומפרט את חוזה הקליטה של כל סוגי ה-intake. הוא אוכף את G2 (מקור-אמת יחיד, אין מסלולים מקבילים) ואת G3 (ingest אחיד ו-idempotent), ונשען על G4 ו-G6.

כשל-השורש שהקובץ מייבש: שני מסלולי ingest לישויות-אחיות שמתפצליםingest_precedent (פסיקה חיצונית) מול ingest_internal_decision (החלטות-ועדה). מסלולים מקבילים גוררים drift: פריט שנקלט במסלול אחד מקבל טיפול שונה מפריט במסלול האחר, והפער מתגלה רק כשרשומה חסרה metadata או לא נמצאת בחיפוש. החוזה כאן מגדיר מסלול קנוני אחד ש-3 סוגי ה-intake עוברים בו.


1. שלושת סוגי ה-intake

סוג-intake מזהה-קנוני קורפוס-יעד מאפיין ייחודי
מסמכי-תיק (case documents) case_number + מזהה-מסמך תיק ערר פעיל משויך לתיק, מסווג לפי סוג-מסמך
פסיקה חיצונית (external precedent) citation (קנוני) case_law (external) staging לפי source_type, ולידציית-enums, citation guard, multimodal
החלטות-ועדה (internal-committee) case_number (קנוני) case_law (internal_committee) staging לפי district, chair_name חובה, גזירת district/proceeding_type

שלושתם הם ישויות-אחיות: אותו טיפוס-עיבוד (קובץ → טקסט → chunks → embeddings → metadata → הלכות), נבדלים בפרמטרים בלבד — לא במסלול-קוד. זוהי משמעות "סימטריה" (חוקה §6).


2. המסלול הקנוני (Canonical Pipeline)

צעדי-העיבוד, בסדר מחייב. כל סוג-intake עובר את אותם צעדים; ההבדל הוא אילו פרמטרים מוזרקים בקלט, לא אילו צעדים מורצים.

  1. Stage file — העתקה דטרמיניסטית לאחסון המתמיד. נתיב-ה-staging הוא פרמטר (source_type לפסיקה חיצונית, district להחלטות-ועדה), לא ענף-קוד נפרד.
  2. Extract textextractor.extract_text(text, page_count, page_offsets). טקסט ריק = כשל מדווח (לא בליעה שקטה; חוקה §6).
  3. Strip Nevo preambleextractor.strip_nevo_preamble להסרת עטיפת-Nevo. אחיד לכל סוג.
  4. Chunk — היררכי (chunk_document_hierarchical) אם PARENT_DOC_RETRIEVAL_ENABLED, אחרת שטוח (chunk_document). אותו ענף-flag בדיוק לכל סוג — בורר הצ'אנקינג נגזר מ-config, לא מסוג-ה-intake.
  5. Embedembeddings.embed_texts(..., input_type="document") ל-children (היררכי) או לכל ה-chunks (שטוח).
  6. Store chunksstore_precedent_chunks_hierarchical או store_precedent_chunks.
  7. Page-image embed (multimodal) — אם MULTIMODAL_ENABLED וגם הקובץ PDF וגם page_count>0: הטמעת עמודי-תמונה (_embed_precedent_pages). non-fatal: מסלול-הטקסט כבר הצליח. התנאי אחיד — הפעלה תלויה ב-flag+סוג-קובץ, לא בסוג-ה-intake.
  8. Queue metadata extractionrequest_metadata_extraction(case_law_id). נדרש לכל סוג שתומך במטא-דאטה (ראה INV-ING3).
  9. Queue halacha extractionrequest_halacha_extraction(case_law_id).
  10. Set statusesextraction_status=completed, halacha_status=pending. החילוץ ה-LLM-י (metadata + הלכות) רץ בנפרד מ-Claude Code המקומי (precedent_process_pending), כי claude CLI אינו זמין בקונטיינר.

צעדים שחייבים להיות אחידים בכל סוג (תיקון האסימטריה): 2 (extract), 3 (strip-Nevo), 4 (בורר-chunk לפי flag), 56 (embed+store), 7 (multimodal — לפי flag+PDF, לא לפי סוג), 89 (תיזמון שני החילוצים), 10 (statuses). מה שרשאי להשתנות לפי סוג: נתיב-ה-staging (צעד 1), ולידציות-קלט ספציפיות, וגזירת-שדות (district/proceeding_type) — אלו פרמטרים של אותו מסלול, לא מסלול נפרד.


3. Invariants של התחום

INV-ING1: מסלול-קליטה קנוני יחיד

כלל: כל סוגי ה-intake (מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה) זורמים דרך פונקציית- קליטה קנונית אחת. סוג-intake חדש מורחב דרך פרמטרים של אותה פונקציה — לעולם לא דרך פונקציה מקבילה. נתון-נגזר (district, proceeding_type) מחושב בתוך המסלול, לא בענף נפרד. מקורות: Martin Kleppmann, DDIA (O'Reilly, 2017 — system of record יחיד) · Martin Fowler (Canonical Data Model) · SSOT (Single Source of Truth) | סטטוס: verified אכיפה: ביקורת-ארכיטקטורה + כלל-הנדסה "סימטריה" (חוקה §6); הקליטה מתנקזת לפונקציה אחת שמקבלת פרמטרי-סוג. אוכף את G2. הפרה ידועה: היום קיימים שני מסלולים — ingest_precedent (precedent_library.py:88) ו-ingest_internal_decision (internal_decisions.py:73) — שמשכפלים את צעדי 210 ומתפצלים בפרטים → ממצא ל-audit.

INV-ING2: קליטה idempotent על המזהה הקנוני

כלל: הקליטה היא idempotent על המזהה הקנוני (citation לפסיקה חיצונית, case_number להחלטות-ועדה ולמסמכי-תיק). קליטה חוזרת של אותו פריט = upsert — אין רשומה כפולה ואין chunks כפולים; התוצאה זהה. מקורות: Martin Kleppmann, DDIA (idempotence & exactly-once) · Stripe / CDC idempotency-key pattern · ISO 8000 (Data quality) | סטטוס: verified אכיפה: מפתח-upsert דטרמיניסטי על המזהה הקנוני בנקודת-הקליטה (create_external_case_law / create_internal_committee_decision) + ולידציית-כתיבה; קשור ל- X1-identifiers.md (נרמול בכתיבה). אוכף את G3. הפרה ידועה: 3 החלטות "סופר" נקלטו ב-3 פורמטים (8126/24, ציטוט-מלא כ-case_number) — היעדר מפתח-upsert דטרמיניסטי גרר רשומות-כפל במקום עדכון → ממצא ל-audit.

INV-ING3: תור חילוץ מטא-דאטה + הלכות לכל סוג

כלל: חילוץ-מטא-דאטה וגם חילוץ-הלכות מתוזמנים (queue) עבור כל סוג-intake שתומך בהם — תיזמון אחיד, לא מותנה במסלול. שני התורים נפתחים יחד בסיום העיבוד הלא-LLM-י. מקורות: ISO 8000 (completeness) · DAMA-UK Six Primary Dimensions for Data Quality (2013, completeness) · Martin Fowler (quality-at-source) | סטטוס: verified אכיפה: קריאה ל-request_metadata_extraction ו-request_halacha_extraction בנקודת-סיום-הקליטה, לכל סוג; חוזה-שלמות יסמן רשומה ללא מטא-דאטה כלא-שמישה (G4, מפורט ב- 02-data-model.md). הפרה ידועה: המסלול הפנימי (internal_decisions.py:208) מתזמן רק request_halacha_extraction ואינו קורא ל-request_metadata_extraction (בניגוד ל-precedent_library.py:292-293 שקורא לשניהם) → ערן סופר 8046/24 נקלטה בלי metadata (headnote/summary/tags ריקים) → ממצא ל-audit.

INV-ING4: re-index בקליטה-חוזרת (upsert ⇒ re-embed)

כלל: קליטה-חוזרת ששינתה את תוכן-הפריט מפעילה re-index — chunks ו-embeddings ישנים נמחקים ונבנים מחדש מהתוכן החדש. אין embeddings מיושנים אחרי upsert. מקורות: Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) · RAG freshness (Lewis et al., 2020, NeurIPS) | סטטוס: verified אכיפה: טריגר re-embed בנתיב ה-upsert של הקליטה + בדיקת-בריאות לגילוי drift; מפורט ב-02-data-model.md ו-03-retrieval.md. אוכף את G6. הפרה ידועה:


4. מצב קיים מול יעד — audit-findings

הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. אלו תסמינים לאיחוד תחת המסלול הקנוני, לא התנהגויות תקינות. כל פריט אומת מול הקוד בפועל.

  • חילוץ מטא-דאטה חסר במסלול הפנימי. ראה INV-ING3 (ההפרה המתועדת שם — ערן סופר 8046/24). יעד: צעד 8 (תור חילוץ) אחיד לשני הסוגים.
  • ולידציית-enums א-סימטרית. המסלול החיצוני מוודא practice_area/source_type מול רשימות חוקיות (precedent_library.py:131-134); המסלול הפנימי אינו מוודא enums. יעד: ולידציה אחידה בנקודת-הקליטה (חוזה-שלמות, G4).
  • staging מפוצל. החיצוני עושה stage לפי source_type (precedent_library.py:138); הפנימי עושה stage לפי district (internal_decisions.py:113-115). יעד: נתיב-staging כפרמטר של המסלול הקנוני (צעד 1), לא ענף-קוד.
  • גזירת-שדות רק במסלול הפנימי. הפנימי גוזר district מ-court (:104) ו-proceeding_type מ-appeal_subtype/case_name (:105), ודורש chair_name (:134). החיצוני אינו גוזר אלו. יעד: גזירה כפרמטר אופציונלי של המסלול הקנוני (שדות-סוג, לא מסלול-סוג).
  • citation guard רק במסלול החיצוני. החיצוני חוסם ציטוט שמתחיל ב-ערר/בל"מ ומפנה למסלול הפנימי (precedent_library.py:124-130). היעד שומר על השער הזה כניתוב-סוג בתוך המסלול הקנוני, לא כהפרדת-פונקציות.
  • multimodal page-image embed רק במסלול החיצוני. החיצוני מטמיע עמודי-תמונה כש- MULTIMODAL_ENABLED + PDF (precedent_library.py:272-278); הפנימי אינו מטמיע עמודי-תמונה. יעד: צעד 7 אחיד — מותנה ב-flag+סוג-קובץ בלבד.
  • fallback case_name→citation רק במסלול החיצוני. החיצוני נופל ל-citation כשם כשחסר case_name (precedent_library.py:158); הפנימי נופל ל-case_number (internal_decisions.py:130). יעד: מדיניות-fallback אחת לשם-תצוגה במסלול הקנוני.

5. הפניות-אחיות

  • 00-constitution.md — invariants גלובליים + כללי-הנדסה.
  • 02-data-model.md — סכמת-האחסון + חוזה-שלמות שאוכף את תוצרי הקליטה.
  • 03-retrieval.md — אחזור, re-index, eval — היעד של ה-chunks הנקלטים.
  • X1-identifiers.md — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-ING2).
  • X5-audit-provenance.md — שלמות-רשומה + עקיבוּת-מקור של פריט נקלט.