fix(aggregator): צד שלם נמחק בשקט — chunking לפי גודל + כשל שמדווח (#233) #430

Merged
chaim merged 1 commits from worktree-aggregator-chunking into main 2026-08-05 10:08:16 +00:00
Owner

הבעיה

בתיק 1069-04-26 נשלחו 310 טענות עוררים בקריאת-Claude אחת. הקריאה החזירה לא-JSON, הקוד רשם warning והחזיר [], והפעולה דיווחה:

status: completed
propositions_processed: 495     ← "עיבדתי הכל"
by_party: committee=27 · permit_applicant=24 · respondent=5 · appellant=0

הצד המרכזי בערר נעלם, והסטטוס אמר שהכול תקין.

נשללו כסיבה: באג פיצול-המשיבים (#224 — הוא דווקא עבד), ניקוי נספח 7, ו-timeout (אומת בהרצה ישירה מהמארח בלי הגבלה).

שתי תקלות מובחנות — שתיהן מתוקנות

1 · הקריאה גדולה מדי

הפיצול הפר-כתב-טענות שנוסף למשיבים ב-#224 הסתיר את זה במקרה — הוא שמר על קריאות קטנות. אבל עוררים וּועדה "speak with a single voice" ואינם מפוצלים לעולם, כך שערר גדול יוצא בקריאה אחת ענקית.

וההערה בקוד כבר תיעדה בדיוק את הכשל הזה, אצל צד אחר:

"a single 100+ proposition call previously returned non-JSON and silently dropped the whole side"

התיקון ההוא פשוט לא הוחל על העוררים. עכשיו כל צד מעל MAX_PROPS_PER_CALL=80 נצבר בכמה קריאות ומשורשר.

המחיר, במפורש: כל chunk מקובץ בבידוד, ולכן צד שמתפצל עלול לקבל יותר טיעונים — וחופפים במקצת — מאשר במעבר יחיד. לאבד ליטיגנט שלם גרוע יותר, והחלופה (פרומפט קטן לכל פרופוזיציה) הייתה מנוונת כל תיק כדי לתקן את הגדולים. החיתוך שומר על הסדר, כי טענות מגיעות ממוינות לפי claim_index ושכנות שייכות בדרך-כלל לאותו ראש-טיעון.

2 · הכשל נבלע

return [] על תשובה לא-רשימה אינו ניתן להבחנה מ"לצד הזה אין טיעונים". עכשיו נזרקת AggregationFailed, הקורא רושם ב-errors, והסטטוס יורד ל-completed_with_errors (כלל-הנדסה §6).

ההודעה נוקבת בשם הצד ובמספר הפרופוזיציות — אחרת מפעיל שרואה completed_with_errors לא יודע איזה ליטיגנט נעלם.

מבחני רגרסיה

לא היה קובץ-מבחן לצובר כלל. נוסף:

✓ chunking: 310 → 4 קריאות · אפס אובדן · הסדר נשמר
✓ תשובה לא-רשימה זורקת AggregationFailed ומזכירה 'appellant'
2 passed

בקובץ יש הערה מפורשת: אם המבחן השני יחזור אי-פעם לטעון == [] — באג הבליעה הוחזר.

Invariants

  • כלל-הנדסה §6 — אין בליעה שקטה.
  • G1 — תיקון במקור (גודל הקריאה), לא בקריאה.
  • G2 / G12 — לא נגועים; שני השערים ירוקים.

אחרי המיזוג

להריץ aggregate_claims_to_arguments("1069-04-26", force=true) ולאמת שכל ארבעת הצדדים מיוצגים, ואז validate_decision למדד-כיסוי אמיתי לטיוטה שכבר נכתבה.

## הבעיה בתיק **1069-04-26** נשלחו **310 טענות עוררים בקריאת-Claude אחת**. הקריאה החזירה לא-JSON, הקוד רשם `warning` והחזיר `[]`, והפעולה דיווחה: ``` status: completed propositions_processed: 495 ← "עיבדתי הכל" by_party: committee=27 · permit_applicant=24 · respondent=5 · appellant=0 ``` **הצד המרכזי בערר נעלם, והסטטוס אמר שהכול תקין.** נשללו כסיבה: באג פיצול-המשיבים (#224 — הוא דווקא עבד), ניקוי נספח 7, ו-timeout (אומת בהרצה ישירה מהמארח בלי הגבלה). ## שתי תקלות מובחנות — שתיהן מתוקנות ### 1 · הקריאה גדולה מדי הפיצול הפר-כתב-טענות שנוסף למשיבים ב-#224 **הסתיר את זה במקרה** — הוא שמר על קריאות קטנות. אבל עוררים וּועדה "speak with a single voice" ואינם מפוצלים לעולם, כך שערר גדול יוצא בקריאה אחת ענקית. וההערה בקוד כבר תיעדה בדיוק את הכשל הזה, אצל צד אחר: > *"a single 100+ proposition call previously returned non-JSON and **silently dropped the whole side**"* **התיקון ההוא פשוט לא הוחל על העוררים.** עכשיו כל צד מעל `MAX_PROPS_PER_CALL=80` נצבר בכמה קריאות ומשורשר. **המחיר, במפורש:** כל chunk מקובץ בבידוד, ולכן צד שמתפצל עלול לקבל יותר טיעונים — וחופפים במקצת — מאשר במעבר יחיד. לאבד ליטיגנט שלם גרוע יותר, והחלופה (פרומפט קטן לכל פרופוזיציה) הייתה מנוונת כל תיק כדי לתקן את הגדולים. החיתוך שומר על הסדר, כי טענות מגיעות ממוינות לפי `claim_index` ושכנות שייכות בדרך-כלל לאותו ראש-טיעון. ### 2 · הכשל נבלע `return []` על תשובה לא-רשימה **אינו ניתן להבחנה** מ"לצד הזה אין טיעונים". עכשיו נזרקת `AggregationFailed`, הקורא רושם ב-`errors`, והסטטוס יורד ל-`completed_with_errors` (כלל-הנדסה §6). ההודעה **נוקבת בשם הצד ובמספר הפרופוזיציות** — אחרת מפעיל שרואה `completed_with_errors` לא יודע איזה ליטיגנט נעלם. ## מבחני רגרסיה לא היה קובץ-מבחן לצובר כלל. נוסף: ``` ✓ chunking: 310 → 4 קריאות · אפס אובדן · הסדר נשמר ✓ תשובה לא-רשימה זורקת AggregationFailed ומזכירה 'appellant' 2 passed ``` בקובץ יש הערה מפורשת: **אם המבחן השני יחזור אי-פעם לטעון `== []` — באג הבליעה הוחזר.** ## Invariants - **כלל-הנדסה §6** — אין בליעה שקטה. - **G1** — תיקון במקור (גודל הקריאה), לא בקריאה. - **G2 / G12** — לא נגועים; שני השערים ירוקים. ## אחרי המיזוג להריץ `aggregate_claims_to_arguments("1069-04-26", force=true)` ולאמת שכל ארבעת הצדדים מיוצגים, ואז `validate_decision` למדד-כיסוי אמיתי לטיוטה שכבר נכתבה.
chaim added 1 commit 2026-08-05 10:07:26 +00:00
fix(aggregator): צד שלם נמחק בשקט — chunking לפי גודל + כשל שמדווח (#233)
All checks were successful
INV-AG3 Agent Tool Grants / agent-tool-grants (pull_request) Successful in 5s
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
Lint — undefined names / undefined-names (pull_request) Successful in 12s
720057bc72
בתיק 1069-04-26 נשלחו 310 טענות עוררים בקריאת-Claude אחת. הקריאה החזירה
לא-JSON, הקוד רשם warning והחזיר [], והפעולה דיווחה status=completed עם
אפס טיעונים לצד המרכזי בערר. 495 propositions_processed — כלומר "עיבדתי
הכל".

שתי תקלות מובחנות, שתיהן מתוקנות:

1. **הקריאה גדולה מדי.** הפיצול הפר-כתב-טענות שנוסף למשיבים (#224) הסתיר
   את זה במקרה — הוא שמר על קריאות קטנות — אבל עוררים וּועדה מדברים בקול
   אחד ואינם מפוצלים לעולם, כך שערר גדול יוצא בקריאה אחת ענקית. ההערה
   בקוד כבר תיעדה בדיוק את הכשל הזה אצל המשיבים; התיקון פשוט לא הוחל על
   העוררים. עכשיו כל צד מעל MAX_PROPS_PER_CALL=80 נצבר בכמה קריאות
   ומשורשר.

   הפיצול הוא trade-off ולא רווח חינם: כל chunk מקובץ בבידוד, ולכן צד
   שמתפצל עלול לקבל יותר טיעונים (וחופפים במקצת) מאשר במעבר יחיד. לאבד
   ליטיגנט שלם גרוע יותר, והחלופה — פרומפט קטן יותר לכל פרופוזיציה —
   הייתה מנוונת כל תיק כדי לתקן את הגדולים. הסדר נשמר בחיתוך, כי טענות
   מגיעות ממוינות לפי claim_index ושכנות שייכות בד"כ לאותו ראש-טיעון.

2. **הכשל נבלע.** החזרת [] על תשובה לא-רשימה אינה ניתנת להבחנה מ"לצד
   הזה אין טיעונים". עכשיו נזרקת AggregationFailed, הקורא רושם אותה
   ב-errors, והסטטוס יורד ל-completed_with_errors (כלל-הנדסה §6).
   ההודעה נוקבת בשם הצד ובמספר הפרופוזיציות, אחרת מפעיל שרואה
   completed_with_errors לא יודע איזה ליטיגנט נעלם.

מבחני רגרסיה: chunking לא מאבד ולא מסדר-מחדש (310→4 קריאות, רצף נשמר);
תשובה לא-רשימה זורקת ומזכירה את שם הצד. אם המבחן השני יחזור אי-פעם
לטעון == [] — באג הבליעה הוחזר.

invariants: כלל-הנדסה §6 — אין בליעה שקטה. G1 — תיקון במקור (גודל הקריאה)
ולא בקריאה. G2/G12 — לא נגועים; שני השערים ירוקים.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chaim merged commit 079a489f0e into main 2026-08-05 10:08:16 +00:00
chaim deleted branch worktree-aggregator-chunking 2026-08-05 10:08:16 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#430