docs(spec): 01-ingest unified intake contract
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
154
docs/spec/01-ingest.md
Normal file
154
docs/spec/01-ingest.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# 01 — קליטה מאוחדת (Unified Ingest Contract)
|
||||||
|
|
||||||
|
קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את **חוזה הקליטה** של כל סוגי
|
||||||
|
ה-intake. הוא אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)
|
||||||
|
(מקור-אמת יחיד, אין מסלולים מקבילים) ואת [G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent)
|
||||||
|
(ingest אחיד ו-idempotent), ונשען על [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)
|
||||||
|
ו-[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן).
|
||||||
|
|
||||||
|
כשל-השורש שהקובץ מייבש: **שני מסלולי 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 text** — `extractor.extract_text` → `(text, page_count, page_offsets)`.
|
||||||
|
טקסט ריק = כשל מדווח (לא בליעה שקטה; חוקה §6).
|
||||||
|
3. **Strip Nevo preamble** — `extractor.strip_nevo_preamble` להסרת עטיפת-Nevo. **אחיד לכל סוג.**
|
||||||
|
4. **Chunk** — היררכי (`chunk_document_hierarchical`) אם `PARENT_DOC_RETRIEVAL_ENABLED`,
|
||||||
|
אחרת שטוח (`chunk_document`). **אותו ענף-flag בדיוק לכל סוג** — בורר הצ'אנקינג נגזר
|
||||||
|
מ-config, לא מסוג-ה-intake.
|
||||||
|
5. **Embed** — `embeddings.embed_texts(..., input_type="document")` ל-children (היררכי)
|
||||||
|
או לכל ה-chunks (שטוח).
|
||||||
|
6. **Store chunks** — `store_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 extraction** — `request_metadata_extraction(case_law_id)`. נדרש לכל
|
||||||
|
סוג שתומך במטא-דאטה (ראה [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)).
|
||||||
|
9. **Queue halacha extraction** — `request_halacha_extraction(case_law_id)`.
|
||||||
|
10. **Set statuses** — `extraction_status=completed`, `halacha_status=pending`.
|
||||||
|
החילוץ ה-LLM-י (metadata + הלכות) רץ בנפרד מ-Claude Code המקומי
|
||||||
|
(`precedent_process_pending`), כי `claude` CLI אינו זמין בקונטיינר.
|
||||||
|
|
||||||
|
> **צעדים שחייבים להיות אחידים בכל סוג (תיקון האסימטריה):** 2 (extract), 3 (strip-Nevo),
|
||||||
|
> 4 (בורר-chunk לפי flag), 5–6 (embed+store), **7 (multimodal — לפי flag+PDF, לא לפי
|
||||||
|
> סוג)**, **8–9 (תיזמון שני החילוצים)**, 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](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים).
|
||||||
|
**הפרה ידועה:** היום קיימים **שני** מסלולים — `ingest_precedent`
|
||||||
|
(`precedent_library.py:88`) ו-`ingest_internal_decision` (`internal_decisions.py:73`) —
|
||||||
|
שמשכפלים את צעדי 2–10 ומתפצלים בפרטים → ממצא ל-[audit](../audit-report.md).
|
||||||
|
|
||||||
|
### 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](X1-identifiers.md) (נרמול בכתיבה). אוכף את
|
||||||
|
[G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent).
|
||||||
|
**הפרה ידועה:** 3 החלטות "סופר" נקלטו ב-3 פורמטים (`8126/24`, ציטוט-מלא כ-`case_number`)
|
||||||
|
— היעדר מפתח-upsert דטרמיניסטי גרר רשומות-כפל במקום עדכון → ממצא ל-[audit](../audit-report.md).
|
||||||
|
|
||||||
|
### 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](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש), מפורט ב-
|
||||||
|
[02-data-model.md](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](../audit-report.md).
|
||||||
|
|
||||||
|
### 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](02-data-model.md) ו-[03-retrieval.md](03-retrieval.md). אוכף את
|
||||||
|
[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן).
|
||||||
|
**הפרה ידועה:** —
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. מצב קיים מול יעד — audit-findings
|
||||||
|
|
||||||
|
הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. **אלו תסמינים לאיחוד תחת המסלול הקנוני,
|
||||||
|
לא התנהגויות תקינות.** כל פריט אומת מול הקוד בפועל.
|
||||||
|
|
||||||
|
- **חילוץ מטא-דאטה חסר במסלול הפנימי.** `ingest_precedent` קורא ל-
|
||||||
|
`request_metadata_extraction` **ו**-`request_halacha_extraction`
|
||||||
|
(`precedent_library.py:292-293`); `ingest_internal_decision` קורא **רק** ל-
|
||||||
|
`request_halacha_extraction` (`internal_decisions.py:208`) — אין `request_metadata_extraction`.
|
||||||
|
זו ההפרה הקונקרטית של [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג) (ערן סופר 8046/24).
|
||||||
|
**יעד:** צעד 8 אחיד לשני הסוגים.
|
||||||
|
- **ולידציית-enums א-סימטרית.** המסלול החיצוני מוודא `practice_area`/`source_type` מול
|
||||||
|
רשימות חוקיות (`precedent_library.py:131-134`); המסלול הפנימי **אינו** מוודא enums.
|
||||||
|
**יעד:** ולידציה אחידה בנקודת-הקליטה (חוזה-שלמות, [G4](00-constitution.md#inv-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](00-constitution.md) — invariants גלובליים + כללי-הנדסה.
|
||||||
|
- [02-data-model.md](02-data-model.md) — סכמת-האחסון + חוזה-שלמות שאוכף את תוצרי הקליטה.
|
||||||
|
- [03-retrieval.md](03-retrieval.md) — אחזור, re-index, eval — היעד של ה-chunks הנקלטים.
|
||||||
|
- [X1-identifiers.md](X1-identifiers.md) — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-ING2).
|
||||||
|
- [X5-audit-provenance.md](X5-audit-provenance.md) — שלמות-רשומה + עקיבוּת-מקור של פריט נקלט.
|
||||||
Reference in New Issue
Block a user