תיעוד תהליך סיווג הצעות חוק חוזרות

April 25, 2026 · View on GitHub

נעשה עבור: פרופ' אמנון מבצע: אמיר טאהורי (עם Claude) תאריך: 2026-04-25 קובץ מסירה: data/snapshots/All_Private_Bills_K1_K25_classified.xlsx


1. שאלת המחקר

לכל הצעת חוק פרטית בכנסות 1–25, לקבוע:

  1. האם זו הצעה מקורית (הוגשה בפעם הראשונה) או הצעה חוזרת (הוגשה אחרי הצעה דומה קודמת).
  2. אם חוזרת — מהי ההצעה המקורית שלה (מספר סידורי פ/NNN + מספר כנסת).

2. מקורות הנתונים

העבודה הסתמכה אך ורק על Knesset OData (API הרשמי של הכנסת). לא על הקובץ המקורי שהתבסס עליו בעבר — הוא נמצא לא אמין (ראה סעיף 6).

מקורשימוש
Knesset OData API (https://knesset.gov.il/Odata/ParliamentInfo.svc)מוזן אל DuckDB warehouse מקומי. מקור כל הנתונים המבניים.
DuckDB warehouse (data/warehouse.duckdb)בסיס נתונים אנליטי מקומי, מכיל את כל טבלאות ה-OData.
KNS_Billטבלת הצעות החוק. זה הקובץ "האמיתי" — 51,673 הצעות חוק פרטיות בכנסות 1-25 (כל מה שיש לכנסת עצמה).
KNS_DocumentBillטבלת קישורי מסמכים. לכל הצעה שומרת URL של מסמך ב-fs.knesset.gov.il (כולל הצעות חוק מלאות עם דברי הסבר).
fs.knesset.gov.ilשרת הקבצים של הכנסת. מכיל את מסמכי ההצעות עצמם (doc/pdf). זה המקור שממנו קראנו דברי הסבר.
pmb.teca-it.com (טל אלוביץ)לא משמש בקובץ הסופי. שימש רק בסיבוב הראשון. סיבובנו העצמאי חליף אותו לחלוטין.

3. השיטה

השיטה היא רפליקציה של שיטת טל ("זיהוי חוזרת לפי דברי ההסבר"), אבל מיושמת אצלנו מ-0. שלבים:

3.1 איתור מסמך לכל הצעה

עבור כל הצעת חוק פרטית ב-KNS_Bill, שולפים את ה-URL של המסמך מ-KNS_DocumentBill, עם סדר עדיפויות:

  1. הצעת חוק לדיון מוקדם (doc/docx) — המסמך העיקרי שמכיל דברי הסבר.
  2. הצעת חוק לקריאה הראשונה — כגיבוי אם הראשון חסר.

3.2 הורדה וחילוץ טקסט

  • הורדה כל המסמך מ-fs.knesset.gov.il עם השהייה של 0.3 שניות בין בקשות (פוליטנס).
  • חילוץ טקסט:
    • .doc / .docx: באמצעות textutil (כלי מובנה של macOS).
    • .pdf: באמצעות pypdf. אם ה-PDF משתמש בקידוד פונט לא סטנדרטי שאי אפשר לפענח, מדלגים.
  • סריקת 4,000 התווים הראשונים של המסמך (דברי הסבר תמיד בתחילת המסמך).

3.3 חיפוש ביטויי חזרה

נסרקים הביטויים הבאים (regex על עברית). הסט הורחב ב-2026-04-21 לאחר בדיקת איכות שחשפה פערים (ראה סעיף 6.3):

ביטוימשמעותהוספה
הצעת חוק דומההצעה דומהמקורי
הצעת חוק זהההצעה זהה (שונה ניסוח מ-"הצעה זהה")2026-04-21
הצעה זהההצעה זההמקורי
חוזרת ומוגשחוזרת ומוגשתמקורי
הוגש\ה? בכנסת ה-NNהפנייה לכנסת מסוימתמקורי
הונחה על שולחן הכנסת ה<שם-כנסת>הסטנדרט של הצהרת חזרה (K16-K25)2026-04-21
בהמשך להצעת חוקבעקבות הצעה קודמתמקורי
ומספרה פ/NNN/Kציטוט ישיר של מספר סידורי + כנסת2026-04-21
פ/NNN או ק/NNNהפנייה ישירה למספר סידורימקורי

אם אף אחד מהביטויים לא מופיע → ההצעה מסווגת כמקורית.

3.4 פתרון ההפניה

אם נמצא פ/NNN + הפניה לכנסת → מחפשים את ההצעה המקורית ב-KNS_Bill (באמצעות PrivateNumber + KnessetNum). המטריצה נפתרת רק אם ההצעה הקודמת באמת קיימת ב-warehouse.

4. תוצאה

4.1 מספרים

מדדערך
סה"כ הצעות פרטיות K1–K2551,673
מקוריות (מתוצאת הסריקה הגולמית)30,386
חוזרות (מתוצאת הסריקה הגולמית)21,287 (41.2%)
מקוריות אפקטיביות אחרי פוסט-עיבוד32,495
חוזרות אפקטיביות אחרי פוסט-עיבוד19,178
הצעות חוזרות שקודמו למקוריות אפקטיביות2,109
כשלי הורדה / ללא URL219 (0.4%)

4.2 שיעור חזרות לפי כנסת

כנסתהצעותחוזרות%הערות
K1–K91,22900.0%לפני עידן הקונבנציה
K1037910.3%
K1170150.7%
K121,48140.3%
K133,59960.2%
K142,64068425.9%נקודת מפנה — תחילת הקונבנציה
K154,1561,39033.5%
K164,6501,88640.6%
K174,0971,91846.8%
K184,6542,03343.7%
K192,9231,54052.7%
K206,0202,60043.2%
K2155443979.2%שיא יחסי — כנסת קצרה
K221,4201,02972.5%
K232,6041,56760.2%
K244,0642,51962.0%
K256,5023,66656.4%

ממצא מעניין: שיעור האזכור המפורש של הצעות קודמות גבוה במיוחד בכנסות קצרות/חוזרות (K21–K22) ונשאר משמעותי גם בכנסות 23–25. המספרים בטבלה הם is_recurring_upstream=True, כלומר זיהוי גולמי מהמסמך לפני פוסט-עיבוד.

5. עמודות בקובץ

קובץ הפלט: data/snapshots/All_Private_Bills_K1_K25_classified.xlsx — 51,673 שורות × 41 עמודות בגיליון Classified Bills, ועוד גיליונות Reference Resolution ו-Data Dictionary.

5.1 זיהוי ההצעה

עמודהתוכן
BillIDמזהה פנימי של הכנסת (מפתח ראשי של KNS_Bill).
KnessetNumמספר הכנסת (1–25).
Nameשם ההצעה בעברית.
PrivateNumberהמספר הסידורי הפרטי (פ/NNN בדברי ההסבר).
SubTypeDescתמיד "פרטית" בקובץ הזה.
doc_urlה-URL של המסמך ב-fs.knesset.gov.il שסרקנו.

בקובץ יש גם עמודות alias ברורות יותר (source_knesset, source_bill_id, source_doc_id, source_url). הן מתארות את מסמך המקור בלבד. הן לא משמשות כראיה להצעת חוק יעד.

5.2 הפניות ישירות מהמסמך

העמודות האלה עונות על השאלה "לאיזו הצעה המסמך עצמו מפנה?". הן נוצרו כדי למנוע בלבול בין ההפניה הישירה לבין שדות הפוסט-עיבוד האפקטיביים.

עמודהתוכן
direct_reference_bill_idBillID של ההפניה הישירה, אם יש מועמד יחיד וברור. ריק כאשר יש כמה מועמדים חזקים באותה מידה.
direct_reference_knesset_numמספר הכנסת של ההפניה הישירה.
direct_reference_private_numberהמספר הפרטי של ההפניה הישירה.
direct_referenceהפניה קריאה בפורמט KnessetNum/PrivateNumber, למשל 15/1423.
cited_reference_countמספר ההפניות שנפתרו מתוך המסמך.
cited_bill_idsכל ה-BillID-ים שנפתרו, מופרדים ב-; .
cited_referencesכל ההפניות הקריאות בפורמט KnessetNum/PrivateNumber, מופרדות ב-; . אם BillID נפתר להצעה ממשלתית ללא PrivateNumber, הוא נשמר ב-cited_bill_ids אך לא מופיע כאן.

דוגמאות שנבדקו בעקבות הערת אמנון:

דוגמהתוצאה בקובץ
הצעת חוק K16/1493direct_reference = 15/1423, cited_references = 15/1423
https://fs.knesset.gov.il//16/law/16_lst_597534.doccited_reference_count = 2, cited_references = 15/1396; 15/3512, ו-ambiguous_reference_resolution=True כי אין מועמד יחיד עדיף.

5.3 סיווג לקידוד (זרימת העבודה)

עמודהתוכן
is_effective_originalTrue אם ההצעה צריכה להיות מקודדת ישירות אחרי פוסט-עיבוד. False אם אפשר להעתיק קידוד מהצעה אפקטיבית אחרת.
effective_original_bill_idBillID של ההצעה האפקטיבית שממנה יש להעתיק קידוד, או BillID של ההצעה עצמה כאשר is_effective_original=True. זה אינו בהכרח ההפניה הישירה במסמך.
effective_original_knesset_numמספר הכנסת של ההצעה האפקטיבית.
effective_original_private_numberהמספר הסידורי של ההצעה האפקטיבית.

5.4 סיווג לניתוח (האמת העובדתית)

עמודהתוכן
is_recurring_upstreamTrue אם הסריקה הגולמית זיהתה את ההצעה כחוזרת (לפני פוסט-עיבוד). שמור בנפרד כדי שהניתוח של "רעש המערכת" (השערת טל) יישאר תקף.
explicit_relation_typeשתי רמות של חזרה לפי הטקסט: identical = הצעה שהוצהרה מפורשות כ"הצעת חוק זהה"; similar = הצעה שהוצהרה כ"הצעת חוק דומה" או "בהמשך להצעת חוק". ריק להצעות שלא זוהה בהן ביטוי חזרה.
final_relation_typeכרגע זהה ל-explicit_relation_type. שמור כעמודת תוצאה סופית כדי לאפשר בעתיד השוואה אחרת, אם תתווסף.
effective_original_reasonמדוע ההצעה "הורמה" ל-is_effective_original=True למרות ש-is_recurring_upstream=True. ערכים עיקריים: doc_no_ancestor_found, ambiguous_doc_reference, suspicious_self_reference_only, doc_fetch_failed, no_doc_url. ריק לרוב ההצעות.

5.5 שקיפות מתודולוגית

עמודהתוכן
methodאיך ההצעה סווגה: doc_no_pattern (לא נמצאה תבנית), doc_pattern_linked (נמצאה תבנית + פתרון ל-BillID), doc_pattern_unresolved (נמצאה תבנית אך לא ניתן לפתור), doc_fetch_failed, no_doc_url.
matched_phraseהביטוי שנמצא (למטרות אודיט).
classification_sourceתמיד doc_based_full בקובץ זה.
reference_candidate_countמספר מועמדי ההפניה הגולמיים שנמצאו.
reference_resolution_reasonהסיבה לפתרון או אי-פתרון ההפניה.
reference_resolution_confidenceציון אמון פנימי לפתרון ההפניה, אם קיים.
target_resolution_statusסטטוס פתרון היעד אחרי ביטול ניחושים לא בטוחים: resolved, סיבות unresolved_*, או not_applicable_no_recurring_phrase.
target_resolution_methodalias ברור יותר ל-reference_resolution_reason.
target_resolution_confidencealias ברור יותר ל-reference_resolution_confidence. זהו confidence היוריסטי, לא אחוז דיוק שנמדד ידנית.
warningsאזהרה קריאה למקרים לא פתורים/עמומים/חשודים.
notesהערת מתודולוגיה קצרה, למשל שהזהות/דמיון מבוססים על ביטוי מפורש ולא על השוואת נוסח משפטי.
multiple_references_detectedהאם נמצאו כמה הפניות במסמך.
suspicious_self_resolutionהאם הופיעה הפניה חשודה לעצמה; הפניות כאלה לא נבחרות כ-direct_reference.
ambiguous_reference_resolutionהאם יש כמה מועמדים חזקים באותה מידה ולכן אין direct_reference יחיד.
ambiguous_reference_reasonהסבר קצר לאמביוולנטיות.
submission_dateתאריך ההנחה/הגשה שחולץ מתחתית המסמך, אחרי סינון תאריכים בלתי אפשריים.

הקובץ לא כולל עוד את עמודת ה-JSON הגולמית reference_candidates: Excel מקצר תאים ארוכים ולכן 37 שורות קיבלו JSON שבור. ההפניות הקריאות למשתמש נשמרות בעמודות direct_reference, cited_bill_ids, ו-cited_references; ה-JSON נשאר בטבלת DuckDB לצורכי אודיט פנימי.

6. מגבלות ידועות

6.1 השיטה שלנו יחסית לטל

השוואה שיטתית קודמת בין הסיווג שלנו לסיווג של טל אלוביץ (K19–K25 בלבד, שם יש חפיפה) עזרה לכייל את השיטה ולמצוא ביטויים חסרים. המספרים הבאים מתעדים את אותה בדיקת כיול היסטורית, ולא מחליפים את אודיט הקובץ הסופי מ-2026-04-24:

  • 24,053 הצעות K19–K25 סווגו על ידי שנינו
  • הסכמה כוללת: 55.3% (13,295 הסכמות מתוך 24,053)
  • 10,652 הצעות: טל מסמנת כחוזרת, אנחנו מסמנים כמקורית (ההבדל הדומיננטי)
  • 106 הצעות: אנחנו מסמנים כחוזרת, טל מסמנת כמקורית (כמעט אפס)

פירוש: כאשר אנחנו כן אומרים "חוזרת", טל מסכימה ב-98.3% מהמקרים (דיוק גבוה מאוד). אבל מהחזרות שטל מוצאת, אנחנו מוצאים רק ~36% (6,009 מתוך 16,738 — recall של 35.9%). טל מוצאת בערך פי 2.8 יותר חזרות מאיתנו.

למה? טל כנראה משתמשת בשיטה סמנטית רחבה מעבר לחיפוש ביטויים מפורשים — ככל הנראה דמיון תוכני של נוסחי ההצעה, השוואת כותרות, או ניתוח מבני. הרגרסיה שלנו (אפילו אחרי הרחבת הביטויים ב-2026-04-21 — סעיף 6.3) תופסת רק חזרות "מוצהרות" (ההצעה מזכירה במפורש את הצעה קודמת בדברי ההסבר). חלק משמעותי מהחזרות הן "משתמעות" — אותו תוכן, ללא הפניה מילולית לקודם.

המשמעות לניתוח ה"רעש": המספר האמיתי של חזרות תלוי בהגדרה. בקובץ הסופי שלנו, is_recurring_upstream=True מייצג חזרה מוצהרת/מזוהה במסמך (21,287 מתוך 51,673; 41.2%). שיטת טל מודדת דמיון סמנטי רחב יותר ולכן עשויה לתת גבול עליון גבוה יותר. לפי שאלת המחקר:

  • אם "חזרה" = "תוכן זהה" → ~58% (שיטת טל) הוא המספר הנכון.
  • אם "חזרה" = "המחוקק מודע שזו חזרה ומצהיר על כך / המסמך מפנה לקודם" → 41.2% בקובץ הסופי הוא המספר הרלוונטי.
  • הפער בין שתי השיטות הוא חזרות משתמעות — הצעה חדשה לפי תוכן קיים אך ללא הפניה מילולית ברורה לקודם.

הקובץ שלנו תומך בשני הגבולות: עמודת is_recurring_upstream מזהה את המוצהרות, ובעזרת our_scan_vs_tal_diff.xlsx ניתן לבחון גם את ההבדלים שמגיעים מטל.

6.2 מגבלות אחרות

  • PDF עם פונטים לא סטנדרטיים: ~0.4% מההצעות שה-PDF שלהן משתמש בקידוד פונט ישן שלא ניתן לפענח בצד הקליינט.
  • קישורי מסמכים חסרים: ~0.05% מההצעות אין להן כלל רשומה ב-KNS_DocumentBill.
  • קישורים שלא ניתנים לשליפה: בקובץ האחרון יש 36 שורות doc_fetch_failed. הן נשארות מסומנות כך ולא מסווגות לפי ניחוש.
  • ללא קישור מסמך: בקובץ האחרון יש 183 שורות no_doc_url. אלה מקרים שבהם אין מסמך מקור לקריאה, ולכן אין בסיס טקסטואלי לקבוע חזרה.
  • הפניות ללא פתרון או אמביוולנטיות: הקובץ לא מנחש כאשר אין מועמד יחיד. במקרים כאלה direct_reference נשאר ריק, cited_references מציג את כל ההפניות שנפתרו, ו-ambiguous_reference_resolution/effective_original_reason מסבירים את המקרה.
  • שלוש הפניות נפתרו ל-BillID ללא מספר פרטי: אלה הצעות ממשלתיות ללא PrivateNumber; הן נשמרות ב-cited_bill_ids, אבל לא ניתן להציג אותן בפורמט KnessetNum/PrivateNumber.

6.3 תשובות לנקודות שעלו בביקורת אמנון

  • הצעות חוק ללא קישור: אין להן מסמך מקור בנתוני הכנסת, או שהמסמך לא ניתן לשליפה. הן מסומנות ב-no_doc_url או doc_fetch_failed; הקובץ לא מנחש את הסיווג שלהן.
  • הדוגמה 11_lst_534232.doc: המסמך הוא מקור מכנסת 11, אבל הטקסט מפנה להצעה בכנסת 10 בלי מספר הצעת חוק ובלי קישור יעד. לכן target_knesset_extracted=10, אך direct_reference ו-resolved_target_bill_id נשארים ריקים, והסטטוס הוא unresolved_no_link_or_number.
  • כמה הפניות באותו מסמך: גיליון Reference Resolution כולל שורה נפרדת לכל הפניה. בגיליון הראשי, cited_references שומר את כולן; direct_reference מתמלא רק כשיש מועמד יחיד וברור.
  • זהה/דומה: הערכים identical ו-similar מבוססים רק על ביטוי מפורש בדברי ההסבר. לא בוצעה השוואה עצמאית של נוסח התיקון עצמו.
  • confidence/דיוק: עמודות ה-confidence הן ציון ביטחון היוריסטי של חילוץ/פתרון ההפניה. הן לא accuracy, precision, recall, או מדידה מול מדגם ידני.

6.4 הרחבת הביטויים (2026-04-21)

הגרסה הראשונה של הרגרסיה דיווחה על recall של 11% בלבד מול טל, ושיעור חזרות כולל של 8.8%. ביקורת איכות (דגימה של 24 הצעות שטל סימנה כחוזרות ואנחנו סימנו כמקוריות) חשפה שכ-29% מהן מכילות הצהרות חזרה מפורשות שהרגרסיה שלנו פספסה — בעיקר את הנוסחה הסטנדרטית של הכנסת:

"הצעת חוק זהה הונחה על שולחן הכנסת [<שם-כנסת>] על ידי [שם חברי כנסת] (פ/NNN/K)"

ביטויים שהיו חסרים:

ביטוי שהתווסףלמה זה חשוב
הצעת חוק זהההנוסחה המקובלת בעברית, שונה מ-"הצעה זהה" שכבר היה ברגרסיה
הונחה על שולחן הכנסת ה<שם-כנסת>הסטנדרט לחלוטין של הצהרת-חזרה בכנסות 16-25 (נבדק מול 24 דוגמאות)
ומספרה פ/NNN/Kציטוט ישיר של המספר הסידורי הקודם

תוצאה לאחר ההרחבה באותו סבב כיול היסטורי:

  • חזרות שזוהו: 4,564 → 12,293 (פי 2.7)
  • Recall מול טל: 11.1% → 35.9% (פי 3.2)
  • Precision: 98.3% (שמרה על דיוק — הביטויים הנוספים לא יצרו false positives)

הקבצים המקוריים (לפני ההרחבה) נשמרו ב-git history. הטבלאות הנוכחיות בקובץ זה משקפות את הרגרסיה המורחבת.

7. איך לשחזר

כל הקוד ב-git תחת AT020993/knesset_refactor. השחזור המלא:

# 1. רענון warehouse מ-Knesset OData (אופציונלי — רק אם רוצים נתונים עדכניים)
cd /Users/amir/Projects/knesset_refactor
source .venv/bin/activate
PYTHONPATH="./src" python -m src.cli refresh

# 2. סריקה מלאה של דברי הסבר עבור כל ה-51,673 הצעות
# ~7 שעות בריצה רצופה; ממשיך מ-cache אם נפסק
PYTHONPATH="./src" python scripts/scan_all_bills.py

# 3. יצירת הקובץ הסופי לאמנון
PYTHONPATH="./src" python scripts/export_all_bills_classified.py

# 4. בדיקות מהירות רלוונטיות
PYTHONPATH="./src" pytest tests/test_recurring_bills_export_resolution.py tests/test_recurring_bills_knesset_docs.py -q

# 5. (אופציונלי) השוואה לטל
PYTHONPATH="./src" python scripts/diff_our_scan_vs_tal.py

8. מבנה הקוד

קובץתפקיד
src/data/recurring_bills/full_scan.pyלוגיקת הסריקה של דברי הסבר
src/data/recurring_bills/knesset_docs.pyהורדה + חילוץ טקסט + regex
scripts/scan_all_bills.pyCLI להפעלת הסריקה המלאה
src/data/recurring_bills/export_resolution.pyנרמול עמודות היצוא, תאריכים, הפניות ישירות/מרובות, ופוסט-עיבוד אפקטיבי
scripts/export_all_bills_classified.pyיצירת הקובץ הסופי (51,673 שורות)
scripts/diff_our_scan_vs_tal.pyהשוואה שיטתית לטל
tests/test_recurring_bills_export_resolution.pyרגרסיות ליצוא, כולל הפניה ישירה להורה לא פתור והפניות מרובות

9. איכות הנתונים

בדיקות השלמות האחרונות לקובץ שנשלח (2026-04-25 15:23 +0300) עברו:

  • 51,673 שורות — התאמה מלאה ליקום ההצעות הפרטיות ב-KNS_Bill ולטבלת bill_classifications_doc_full.
  • 0 כפילויות BillID.
  • 0 הפניות ישירות לעצמן.
  • 0 הפניות מצוטטות לעצמן.
  • 0 שורות doc_pattern_linked ללא direct_reference.
  • 0 שורות חוזרות (is_recurring_upstream=True) עם explicit_relation_type ריק.
  • 0 תאריכי submission_date עתידיים/בלתי אפשריים/מחוץ לתקופת הכנסת.
  • 0 נוסחאות Excel.
  • אין עמודות original_* או is_original מבלבלות בקובץ; יש רק effective_original_* ו-is_effective_original.
  • אין עמודת JSON גולמית שעלולה להיחתך ב-Excel.
  • 0 ערכי same_knesset_name_fallback.
  • הקובץ הנוכחי זהה ערכית ליצוא טרי מה-warehouse.

פקודות אימות שרצו:

uvx ruff check scripts/export_all_bills_classified.py src/data/recurring_bills/export_resolution.py tests/test_recurring_bills_export_resolution.py
uvx ruff format --check scripts/export_all_bills_classified.py src/data/recurring_bills/export_resolution.py tests/test_recurring_bills_export_resolution.py
uv run --extra dev mypy scripts/export_all_bills_classified.py src/data/recurring_bills/export_resolution.py tests/test_recurring_bills_export_resolution.py
PYTHONPATH="./src" python -m pytest tests/ --ignore=tests/test_api_integration.py --ignore=tests/test_e2e.py --ignore=tests/test_data_pipeline_integration.py --ignore=tests/test_connection_leaks.py --tb=short -q

נספח: למה אנחנו עושים את זה ככה?

שאלה שעלתה: למה לא להשתמש ב-API של טל ישירות?

תשובה: טל היא צד שלישי. אם ה-API שלה יירד, או אם היא תשנה שיטה, או אם יש אי-הסכמה מתודולוגית — אין לנו שליטה. סריקה עצמאית על fs.knesset.gov.il נותנת לנו דאטה-סט משלנו שאנחנו יכולים לתעד, להעריך, ולשחזר. מצד שני — אין משמעות שטל טועה. יש כאן רק שתי שיטות שמודדות דברים שונים במקצת:

  • שלנו: "האם ההצעה מצהירה שהיא חוזרת?" (syntactic recurrence)
  • טל: "האם ההצעה דומה מהותית להצעה קודמת?" (semantic recurrence)

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