Documents the rules and decisions behind building DOCX files from דפנה's
decision template (טיוטת החלטה.dotx). The implementation lives in
mcp-server/src/legal_mcp/services/analysis_docx_exporter.py; this skill
captures the "why" so future improvements don't need to rediscover it.
Contents:
SKILL.md 5 critical rules, style mapping table,
export flow, line classification,
dash policy, placeholder handling,
troubleshooting, future TODOs
references/dotx-to-docx.md why python-docx can't open .dotx +
the conversion recipe
references/rtl-runs.md why <w:rtl/> is required on every run
(otherwise Hebrew falls back to
Times New Roman)
references/style-mapping.md XML dump of every template style,
with the Title-via-theme gotcha
references/line-classification.md the 7 regex categories in
_classify_line() with real examples
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
name, description
| name | description |
|---|---|
| dafna-decision-template | ייצוא מסמכי DOCX עבור ועדת ערר לתכנון ובניה מחוז ירושלים (יו"ר עו"ד דפנה תמיר), באמצעות שימוש בסגנונות המוגדרים בטמפלט Word של דפנה (`טיוטת החלטה.dotx`). הסקיל מחיל את סגנונות הטמפלט (Normal/Heading 1/Heading 2/Quote/List Paragraph) על תוכן שנכתב מתוכנתית — בלי להגדיר פונט/גודל/RTL/שוליים ידנית. טריגרים: "ייצוא החלטה", "ייצוא ניתוח משפטי", "DOCX של דפנה", "טמפלט החלטה", "סגנונות החלטה", "Word עם David", "מסמך ועדת ערר", "הורדת החלטה", "פסקה ממוספרת בעברית", "(א) (ב) (ג)", כל בקשה להוציא מסמך Word המבוסס על טמפלט דפנה. |
Dafna Decision Template — ייצוא DOCX מסגנונות טמפלט
מה זה עושה
סקיל זה הוא layer דק מעל python-docx שמטעין את הטמפלט של דפנה
(skills/docx/decision_template.docx —
מומר מ-data/training/טיוטת החלטה.dotx) וכותב תוכן חדש על בסיסו, על ידי
שיוך שמות סגנונות בלבד (paragraph.style = "Heading 2"). העיצוב —
פונט David, RTL, גדלים, הזחות, מספור אוטומטי — מגיע מה-styles.xml
של הטמפלט.
השירות המעשי נמצא ב-
mcp-server/src/legal_mcp/services/analysis_docx_exporter.py.
הסקיל מתעד את הכללים שה-service מיישם, כך שניתן לשחזר/להרחיב אותם
בסקריפטים אחרים ללא צורך לגלות הכל מחדש.
🔴 קריטי — 5 עקרונות שחייבים לזכור
1. לא להגדיר font/size/indent ידנית — תמיד style
# ❌ אל
run.font.name = "David"; run.font.size = Pt(13)
paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT
# ✅ כן
paragraph = doc.add_paragraph(style="Normal") # כל השאר מגיע מהטמפלט
2. RTL חייב דגלים ב-paragraph וב-run — גם אם הסגנון כולל bidi
# ❌ אל — עברית תצא ב-Times New Roman כי Word לא יודע שזה complex-script
p = doc.add_paragraph(style="Normal")
p.add_run("טקסט בעברית")
# ✅ כן — מסמנים את ה-run כ-RTL
p = doc.add_paragraph(style="Normal")
_mark_paragraph_rtl(p)
run = p.add_run("טקסט בעברית")
_mark_run_rtl(run) # ← מוסיף <w:rtl/> ל-rPr
בלי הדגל הזה, Word נופל בחזרה ל-ascii font (Times New Roman) במקום
ל-cs font (David). זו הסיבה הנפוצה ביותר לתוצאה "עברית ב-Times New Roman".
ראה references/rtl-runs.md לפרטים מלאים.
3. .dotx לא נטען ישירות — יש להמיר ל-.docx פעם אחת
python-docx פותח רק .docx. להמרה: scripts/convert_decision_template.py
(בשורש הפרויקט). יש להסיר:
word/glossary/*parts- Override entries שמצביעים אליהם ב-
[Content_Types].xml - Relationship
glossaryDocumentב-word/_rels/document.xml.rels - להחליף content-type מ-
...template.main+xmlל-...document.main+xml
ראה references/dotx-to-docx.md.
4. Title לא טוב ככותרת עברית — השתמש ב-Heading 1
הסגנון Title בטמפלט מפנה ל-theme fonts (majorFont). ב-theme1.xml:
majorFont.latin = "Aptos Display", majorFont.cs = "" (ריק).
לכן עברית תרונדר ב-Latin fallback.
Heading 1 יורש cs="David" מ-Normal — השתמש בו לכותרת ראשית.
5. מספור אוטומטי רק ב-List Paragraph — decimal בלבד
List Paragraph (styleId a0) מקושר ל-numId=1 → numFmt=decimal.
כלומר Word יוסיף אוטומטית "1.", "2.", "3." לכל פסקה עם הסגנון הזה.
- שורות שמתחילות ב-
N.→ הסר את המספר מהטקסט, החלList Paragraph. - שורות שמתחילות ב-
(א)(ב)→ השתמש ב-List Paragraphעם הסרת<w:numPr>, כי המספור בעברית נכתב בעצמו על ידי המחבר. - Bullets (
-,•) → הסר את הסימן, השארNormal.
ראה references/style-mapping.md.
הטמפלט — מיפוי סגנונות
מיפוי מלא של הסגנונות בטמפלט ל-content type. זה ה"חוזה" של הסקיל — כל שינוי דרך השירות צריך להיצמד אליו.
| תוכן | style name | הערה |
|---|---|---|
| כותרת מסמך ("ניתוח משפטי וכתיבת עמדה בערר X") | Heading 1 |
יורש cs="David" |
| כותרת מקטע ראשי (רקע דיוני, פסיקה כללית, סוגיות להכרעה, מסקנות) | Heading 2 |
bold + underline |
| כותרת משנה בתוך סוגיה (טענה (claim), תשובה, ניתוח, נקודות פתוחות, …) | Heading 2 |
|
| שם subsection (טענת סף 1, סוגיה 2) | Normal + bold run |
|
| פסקת רקע רגילה | Normal |
David 13pt, justify, RTL |
| פסקת רשימה ממוספרת (1., 2., 3.) | List Paragraph |
Word ימספר |
| פסקת רשימה בעברית ((א), (ב)) | List Paragraph + _strip_numpr() |
המספור נשאר בטקסט |
| ציטוט פסיקה | Quote |
bold + הזחה |
| citation / מקור | Normal + italic |
ראה references/style-mapping.md לטבלה מורחבת עם ה-XML של כל סגנון.
תוכן המקור — analysis-and-research.md
הקובץ נכתב על ידי legal-analyst agent. מבנה מצופה:
# ניתוח משפטי וכתיבת עמדה — ערר {case_number}
תאריך: DD.MM.YYYY
## רקע דיוני
{prose content}
## עובדות מוסכמות
1. ...
## עובדות שנויות במחלוקת
1. ...
## טענות סף
### טענה {n}: {title}
**עמדת המבקשת:** ...
**עמדת ועדת הערר:** [ימולא ע"י יו"ר הוועדה]
## סוגיות להכרעה
### סוגיה {n}: {title}
**ממצאים עובדתיים:**
...
**טענה (claim):**
העורר טוען כי:
(א) ...
(ב) ...
**ניתוח:**
...
- **נקודות פתוחות:**
1. ...
- **הערכה ראשונית:** {prose}
**עמדת ועדת הערר:** [ימולא ע"י יו"ר הוועדה]
## מסקנות
...
ה-parser (research_md.py) חולק את זה ל-threshold_claims[], issues[],
prose sections, ו-conclusions. חשוב: שמות sections חייבים להכיל את
המילים המופתח (רקע דיוני, טענות סף, …) — ה-parser עובד לפי matching של
keywords, לא לפי מספר ה-H2.
זרימת הייצוא
סדר המקטעים ב-DOCX (גם אם סדר ה-H2 ב-MD שונה):
- כותרת (Heading 1) + שורת תאריך
- רקע (Heading 2) —
represented_party,procedural_background,agreed_facts,disputed_facts(אם קיימים) - פסיקה כללית (Heading 2) —
case_precedentsעםsection_id=NULL - טענות סף (Heading 2) — לכל subsection: title, fields, chair_position, precedents
- סוגיות להכרעה (Heading 2) — אותה חלוקה
- מסקנות (Heading 2) — בסוף
הסיבה: בקריאה משפטית נכון להציג תחילה רקע ועובדות, ואז את הדיון. פסיקה כללית מופיעה לפני הסוגיות כי היא רוחבית.
עיבוד שורות — _classify_line()
ה-service מפרש כל שורה של content לאחת מ-6 קטגוריות:
| kind | דוגמה | מה קורה |
|---|---|---|
label_heading |
**נקודות פתוחות:** (שורה שלמה, כולל - **X:**) |
Heading 2 |
label_heading (plain) |
העורר טוען כי: (שורה קצרה שמסתיימת ב-:) |
Heading 2 |
inline_label |
**שאלה עקרונית:** מה... |
Normal עם label bold inline |
numbered |
1. הנספח אינו מחייב |
List Paragraph, המספר מוסר |
bullet |
- nevo (קלאסי)... |
Normal, הסימן מוסר |
heb_letter |
(א) הנספח אינו... |
List Paragraph + strip numPr |
plain |
רגיל | Normal |
בנוסף, inline **...** מעובד בכל ריצה דרך _add_runs_with_inline_bold
— כל **word** הופך ל-run נפרד עם bold=True.
ראה references/line-classification.md.
מקפים — מדיניות
המשתמש (דפנה) ביקשה: "לא רוצה מקפים בכלל". הסקיל מסיר:
—(em-dash, U+2014)–(en-dash, U+2013)
מכל טקסט שהקוד כותב למסמך (גם תוכן מהמקור). מקפים רגילים (-)
נשמרים. הפונקציה: _no_dash().
שדות ריקים — placeholder
שדה chair_position (עמדת ועדת הערר) שמכיל אחד מהסימנים הריקים
([ימולא ע"י יו"ר הוועדה], [טרם מולא], וכד') → מוחלף ב-
[טרם מולאה עמדת ועדת הערר] בסגנון italic. זה סימן ויזואלי ברור
שנשאר עדיין להשלים.
שימוש — API
from legal_mcp.services.analysis_docx_exporter import build_analysis_docx
path = await build_analysis_docx("8070-25")
# → data/cases/8070-25/exports/ניתוח-משפטי-v{N}.docx
Endpoint ציבורי: GET /api/cases/{case_number}/research/analysis/export-docx
התמודדות עם בעיות נפוצות
"עברית יוצאת ב-Times New Roman"
- חסר
<w:rtl/>ב-run. הוסף_mark_run_rtl(run)אחרי כלadd_run. - בדוק: הסגנון שאתה משתמש בו יש בו
cs="David"(או יורש מ-Normal שיש לו)?
"המספור כפול: '1. (א) ...'"
- אתה משתמש ב-
List Paragraphעל שורה עם(א). צריך_strip_numpr(para).
"כוכביות **...** מופיעות במסמך"
- הקפד להעביר תוכן דרך
_add_runs_with_inline_bold(), לאparagraph.add_run().
"התבנית לא נטענת"
- הרץ מחדש
python scripts/convert_decision_template.py. בדוק שה-docx שנוצר פותחיb ב-Word ללא שגיאות.
"המספור ברשימה השנייה ממשיך מהראשונה (4,5,6 במקום 1,2,3)"
- ידוע. הפתרון: להוסיף override של
numIdברמת ה-paragraph הראשון של הרשימה החדשה. עדיין לא מיושם — ראה "שיפורים עתידיים".
שיפורים עתידיים (TODO)
- Reset של מספור List Paragraph בין רשימות נפרדות (לא רציף).
- תמיכה ב-
פיסקת רשימה - ללא מספור(styleId-) לbullets. - עיצוב מותאם לסוג הערר (1xxx/8xxx/9xxx) — כרגע אחיד.
- אפשרות להוריד רק מקטעים נבחרים (רק טענות סף, רק מסקנות, וכו').
מבנה קבצים
skills/dafna-decision-template/
├── SKILL.md ← הקובץ הזה
└── references/
├── dotx-to-docx.md ← איך ממירים .dotx ל-.docx
├── rtl-runs.md ← למה `<w:rtl/>` חשוב בכל run
├── style-mapping.md ← מיפוי מלא של סגנונות הטמפלט
└── line-classification.md ← לוגיקת _classify_line()
mcp-server/src/legal_mcp/services/
└── analysis_docx_exporter.py ← המימוש המעשי
scripts/
└── convert_decision_template.py ← המרת dotx → docx (חד-פעמי)
skills/docx/
└── decision_template.docx ← הטמפלט המומר (artifact)
היסטוריה
| גרסה | תאריך | שינוי |
|---|---|---|
| v1.0 | 2026-04-16 | יצירת הסקיל. מיפוי ראשוני של סגנונות, תמיכה ב-RTL runs, inline bold, (א)(ב), Heading 1/2, מקטעי רקע. |