feat(precedents): citation_formatted דטרמיניסטי בקוד — Gemini מחלץ רכיבים, לא מעצב (#145) #262

Merged
chaim merged 1 commits from worktree-precedent-deterministic-citation into main 2026-06-15 03:38:51 +00:00
Owner

הבעיה (#145)

מחלץ-המטא ביקש מ-Gemini Flash לעצב את מראה-המקום המלא (citation_formatted). ב-JSON-mode חופשי (ללא responseSchema) המודל החזיר JSON תקין ומלא אך השמיט בעקביות דווקא את השדה הזה — אומת על 8070-05-25, 1194-12-25, 1200-12-25, וגם כשהצדדים זוהו (case_name_short). זה השדה הקשה ביותר (עיצוב מחרוזת Markdown) והפרומפט התיר להשאירו ריק → Flash מפיל אותו בכל הרצה.

הפתרון

citation_formatted הוא שדה-תצוגה נגזר (X1 §3 / INV-ID2) — מורכב דטרמיניסטית מרכיבים מובְנים, לא מעוצב ע"י LLM. תפקיד ה-LLM מצטמצם לחילוץ רכיבים אמינים (קריאת-כותרת: שורת-הצדדים + קידומת-ההליך לפסקי בית-משפט). אותה פילוסופיה כמו format_plan_citation הקיים לבלוק ט.

  • db.format_precedent_citation(record) — מרכיב לפי כללי-הציטוט-האחיד:
    • ועדת-ערר (מחוזית/ארצית/בל"מ) — קידומת מ-proceeding_type, forum מ-district, reporter מ-source_kind (החלטה פנימית = תאריך בלבד; חיצונית/נבו = "נבו ").
    • פסקי בית-משפט (עליון/מנהלי) — קידומת מ-court_prefix (LLM) + קיצור-מחוז.
    • מוציא docket נקי מ-case_number מזוהם (עע"מ 683/13683/13) כדי לא לכפול קידומת.
    • נמנע ('') כשחסר רכיב (צדדים/docket/תאריך/קידומת) — abstention על המצאה (INV-AH).
  • case_law.parties (V39) — שורת "עורר נ' משיב" כבסיס re-derivable; הרכיב המודגש של הציטוט.
  • מחלץ-המטא — הפרומפט מחלץ parties + citation_prefix (לא citation_formatted); apply_to_record מרכיב דטרמיניסטית מהרשומה-האפקטיבית וממלא רק שדה ריק (עריכות-יו"ר ב-/precedents/[id] נשמרות).
  • scripts/backfill_precedent_citations.py — backfill 2-מעברים (1: דטרמיניסטי מהשמור, ללא API; 2: LLM כשיש full_text), מדווח שורות-נמנעות (לא מנחש), idempotent. מטרה: 171 פסקי בית-המשפט הריקים.

אימות

  • שלוש הרשומות שמולאו ידנית קודם — משוחזרות תו-בתו ע"י הפונקציה (4.5.2026 / 19.4.2026 / 27.5.2026).
  • פסק עליון אמיתי מולא end-to-end: LLM→parties="רשות שדות התעופה ואח' נ' אליהו טויטן ואח'", prefix="עע"מ"עע"מ 683/13 **…** (נבו 3.9.2015) (docket נקי, ללא כפילות-קידומת).
  • abstention עובד (אין צדדים → ''; פסק ללא קידומת → '').
  • pytest test_fu2b_reconcile.py ✓ · py_compile ✓ · import ✓.

Invariants

  • INV-ID2 / X1 §3 — מקיים: citation_formatted נשאר שדה-תצוגה נגזר, לעולם לא מפתח.
  • INV-AH — מקיים: אפס המצאה; כל רכיב חסר → abstention.
  • G1 — מקיים: docket נקי לתצוגה; הנרמול-בכתיבה הקיים של case_number ללא-שינוי.
  • G2 — מקיים: מסלול-יחיד — מחליף את נתיב-ה-LLM לציטוט, לא יוצר מקביל.
  • G3 — backfill idempotent (רק שדה ריק).

🤖 Generated with Claude Code

## הבעיה (#145) מחלץ-המטא ביקש מ-Gemini Flash **לעצב** את מראה-המקום המלא (`citation_formatted`). ב-JSON-mode חופשי (ללא `responseSchema`) המודל החזיר JSON תקין ומלא אך **השמיט בעקביות** דווקא את השדה הזה — אומת על `8070-05-25`, `1194-12-25`, `1200-12-25`, וגם כשהצדדים זוהו (`case_name_short`). זה השדה הקשה ביותר (עיצוב מחרוזת Markdown) והפרומפט התיר להשאירו ריק → Flash מפיל אותו בכל הרצה. ## הפתרון `citation_formatted` הוא **שדה-תצוגה נגזר** (X1 §3 / INV-ID2) — מורכב דטרמיניסטית מרכיבים מובְנים, לא מעוצב ע"י LLM. תפקיד ה-LLM מצטמצם לחילוץ רכיבים אמינים (קריאת-כותרת: שורת-הצדדים + קידומת-ההליך לפסקי בית-משפט). אותה פילוסופיה כמו `format_plan_citation` הקיים לבלוק ט. - **`db.format_precedent_citation(record)`** — מרכיב לפי כללי-הציטוט-האחיד: - ועדת-ערר (מחוזית/ארצית/בל"מ) — קידומת מ-`proceeding_type`, forum מ-`district`, reporter מ-`source_kind` (החלטה פנימית = תאריך בלבד; חיצונית/נבו = "נבו "). - פסקי בית-משפט (עליון/מנהלי) — קידומת מ-`court_prefix` (LLM) + קיצור-מחוז. - מוציא **docket נקי** מ-`case_number` מזוהם (`עע"מ 683/13`→`683/13`) כדי לא לכפול קידומת. - **נמנע (`''`)** כשחסר רכיב (צדדים/docket/תאריך/קידומת) — abstention על המצאה (INV-AH). - **`case_law.parties` (V39)** — שורת "עורר נ' משיב" כבסיס re-derivable; הרכיב המודגש של הציטוט. - **מחלץ-המטא** — הפרומפט מחלץ `parties` + `citation_prefix` (לא `citation_formatted`); `apply_to_record` מרכיב דטרמיניסטית מהרשומה-האפקטיבית וממלא **רק שדה ריק** (עריכות-יו"ר ב-`/precedents/[id]` נשמרות). - **`scripts/backfill_precedent_citations.py`** — backfill 2-מעברים (1: דטרמיניסטי מהשמור, ללא API; 2: LLM כשיש full_text), מדווח שורות-נמנעות (לא מנחש), idempotent. מטרה: 171 פסקי בית-המשפט הריקים. ## אימות - שלוש הרשומות שמולאו ידנית קודם — **משוחזרות תו-בתו** ע"י הפונקציה (4.5.2026 / 19.4.2026 / 27.5.2026). - פסק עליון אמיתי מולא end-to-end: LLM→`parties="רשות שדות התעופה ואח' נ' אליהו טויטן ואח'"`, `prefix="עע"מ"` → `עע"מ 683/13 **…** (נבו 3.9.2015)` (docket נקי, ללא כפילות-קידומת). - abstention עובד (אין צדדים → `''`; פסק ללא קידומת → `''`). - `pytest test_fu2b_reconcile.py` ✓ · `py_compile` ✓ · import ✓. ## Invariants - **INV-ID2 / X1 §3** — מקיים: `citation_formatted` נשאר שדה-תצוגה **נגזר**, לעולם לא מפתח. - **INV-AH** — מקיים: אפס המצאה; כל רכיב חסר → abstention. - **G1** — מקיים: docket נקי לתצוגה; הנרמול-בכתיבה הקיים של `case_number` ללא-שינוי. - **G2** — מקיים: מסלול-יחיד — **מחליף** את נתיב-ה-LLM לציטוט, לא יוצר מקביל. - **G3** — backfill idempotent (רק שדה ריק). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-15 03:38:41 +00:00
feat(precedents): citation_formatted דטרמיניסטי בקוד — Gemini מחלץ רכיבים, לא מעצב (#145)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
d6608ce849
הבעיה (#145): מחלץ-המטא ביקש מ-Gemini Flash *לעצב* את מראה-המקום המלא
(citation_formatted). ב-JSON-mode חופשי (ללא responseSchema) המודל החזיר JSON
תקין ומלא אך **השמיט בעקביות** דווקא את השדה הזה — אומת על 8070-05-25,
1194-12-25, 1200-12-25 (וגם כשהצדדים זוהו). השדה הקשה ביותר (עיצוב מחרוזת) +
היתר-בפרומפט להשאיר ריק → Flash מפיל אותו.

הפתרון: citation_formatted הוא **שדה-תצוגה נגזר** (X1 §3 / INV-ID2) — מורכב
דטרמיניסטית מרכיבים מובְנים, לא מעוצב ע"י LLM. תפקיד ה-LLM מצטמצם לחילוץ
רכיבים אמינים (שורת-הצדדים, קידומת-ההליך לפסקי-בית-משפט).

- db.format_precedent_citation(record) — מרכיב לפי כללי-הציטוט-האחיד:
  ועדת-ערר (מחוזית/ארצית/בל"מ) מ-proceeding_type+district+source_kind;
  פסקי-בית-משפט מ-court_prefix(LLM)+district-abbrev. מוציא docket נקי
  מ-case_number מזוהם ("עע\"מ 683/13"→"683/13"). נמנע ('') כשחסר רכיב
  (צדדים/docket/תאריך/קידומת) — abstention על המצאה (INV-AH).
- case_law.parties (V39) — שורת "עורר נ' משיב" כבסיס re-derivable.
- מחלץ-המטא: הפרומפט מחלץ parties+citation_prefix (לא citation_formatted);
  apply_to_record מרכיב דטרמיניסטית מהרשומה-האפקטיבית וממלא רק שדה ריק
  (עריכות-יו"ר נשמרות).
- scripts/backfill_precedent_citations.py — backfill 2-מעברים (דטרמיניסטי→LLM),
  מדווח שורות-נמנעות, idempotent.

אומת: 3 הרשומות הידניות משוחזרות תו-בתו; פסק עליון אמיתי מולא end-to-end
(עע"מ 683/13 ... נבו 3.9.2015). test_fu2b_reconcile ✓.

Invariants: INV-ID2/X1§3 (ציטוט=תצוגה נגזר, לא מפתח) · INV-AH (abstention,
אפס המצאה) · G1 (docket נקי) · G2 (מסלול-יחיד — מחליף את נתיב-ה-LLM, לא מקביל).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit 7043de0ac2 into main 2026-06-15 03:38:51 +00:00
chaim deleted branch worktree-precedent-deterministic-citation 2026-06-15 03:38:52 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#262