Files
legal-ai/docs/spec/05-qa-review.md

196 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 05 — בקרת איכות ושערים אנושיים (QA & Human Review)
קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את שלב **הביקורת** לפני
ייצוא: (1) **שערי-QA אוטומטיים** (`validate_decision` — 6 בדיקות) ו-(2) **שערים אנושיים**
(אישור הלכה, בחירת תוצאה, פידבק היו"ר). הוא אוכף את
[INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)
(שערים אנושיים) ואת [INV-G11](00-constitution.md#inv-g11-תוכן-החלטה-מנומקת) (תוכן מנומק).
> **⚠ קובץ מעורב — שני מודלי-סמכות.** לפי החוקה (§3, §5):
> - **שערי-הממשל** (שערים אנושיים, שער-הייצוא) הם **invariants הנדסיים** במודל
> הממשל-שיפוטי → נושאים `מקורות:` (NCSC/JTC · CEPEJ 2018 · FJC) + `סטטוס: verified`.
> - **מכניקת בדיקות-התוכן** (מה הבדיקה האוטומטית בוחנת בפועל — רקע ניטרלי, ללא כפילות,
> כיסוי-טענות) היא **תוכן-משפטי** → נושאת `מקור-סמכות:` (היו"ר + מסמכי-הפרויקט +
> [04-analysis-writing.md](04-analysis-writing.md)), **ללא** מקורות חיצוניים וללא סטטוס.
---
## 1. שערי-QA אוטומטיים — `validate_decision`
`validate_decision(case_number)` (wrapper ב-`tools/drafting.py:363`, נחשף ב-`server.py:551`)
טוען את בלוקי-ההחלטה והטענות מה-DB ומריץ **6 בדיקות**, אז כותב את התוצאות לטבלת
`qa_results` ומחזיר `passed` / `critical_failures` / `export_blocked`. הליבה:
`services/qa_validator.py:292` (`validate_decision`). כל בדיקה מחזירה
`{name, passed, errors, severity}`; `severity ∈ {critical, warning}`.
> **חישוב החסימה:** `critical_failures = Σ(not passed ∧ severity=="critical")`
> (`qa_validator.py:338`), ו-`export_blocked = critical_failures > 0`
> (`qa_validator.py:362`). בדיקת `warning` שנכשלת מורידה `passed=False` אך **אינה** חוסמת
> ייצוא. ראה [§3 / INV-QA3](#inv-qa3-החלטה-לא-מיוצאת-עם-כשל-קריטי-governance--g10).
### 1.1 ששת השערים
| # | בדיקה | מה בוחנת | severity | פונקציה (file:line) |
|---|-------|----------|----------|---------------------|
| 1 | `neutral_background` | רקע (בלוק ו) ללא מילות-שיפוט (`VALUE_WORDS`) וללא ציטוט-צד (`QUOTE_INDICATORS`) | **warning** | `check_neutral_background``qa_validator.py:66` |
| 2 | `claims_coverage` | כל טענה מבלוק ז נענתה בבלוק י (בדיקה סמנטית דרך Claude) | **critical** | `check_claims_coverage``qa_validator.py:107` |
| 3 | `weight_compliance` | משקל-מילים של כל בלוק בטווח לפי סוג-ערר (`WEIGHT_RANGES`) | **warning** | `check_weight_compliance``qa_validator.py:177` |
| 4 | `structural_integrity` | בלוקי-חובה קיימים (ה, ז, י, יא) + בלוק י הוא הכבד ביותר | **critical** | `check_structural_integrity``qa_validator.py:206` |
| 5 | `no_duplication` | אין משפט מבלוק ו (>30 תווים) שחוזר מילה-במילה בבלוק י | **warning** | `check_no_duplication``qa_validator.py:235` |
| 6 | `sequential_numbering` | מספור-סעיפים רציף בכל הבלוקים, מתחיל ב-1, ללא פערים | **warning** | `check_sequential_numbering``qa_validator.py:261` |
### 1.2 דקויות חשובות (אל תניח — מהקוד)
- **רק 2 שערים קריטיים** חוסמים ייצוא: `claims_coverage` ו-`structural_integrity`. שאר
הארבעה הם `warning` (כולל `neutral_background`!) — `qa_validator.py:86, 202, 240, 286`.
- **`claims_coverage` סובלני ל-20%:** עובר אם `len(missing) ≤ total*0.2`
(`qa_validator.py:170`). מסנן לטענות `appellant`/`respondent` שאינן מבלוק-ז
(`qa_validator.py:120-129`), כי טענות `committee`/`permit_applicant` הן עמדות-הגנה ולא
דורשות מענה. כשל-פענוח של Claude → fallback `passed=True` כדי לא לחסום ייצוא על תקלת-LLM
(`qa_validator.py:148-152`).
- **`neutral_background` ריק = עובר:** בלוק ו ריק/חסר מחזיר `passed=True`
(`qa_validator.py:69`). הבדיקה היא lexical (רשימת-מילים + regex), לא סמנטית.
- **`no_duplication` תופס רק חזרה מילה-במילה** (substring) — לא פרפרזה.
- כל ריצה **מנקה** את `qa_results` הקודמות של התיק ואז כותבת מחדש (`qa_validator.py:344-357`).
### 1.3 שערי-התוכן מתפעלים את WR1WR3
שלוש מ-6 הבדיקות הן ההפעלה האוטומטית (חלקית) של ה-invariants של התוכן ב-
[04-analysis-writing.md](04-analysis-writing.md):
| שער QA | invariant-תוכן | פער (אוטומטי מול הגדרה) |
|--------|----------------|--------------------------|
| `neutral_background` | [INV-WR1](04-analysis-writing.md#inv-wr1-רקע-ניטרלי-בלוק-ו--עובדות-בלבד) | lexical בלבד — לא תופס שיפוט עקיף; warning, לא critical |
| `no_duplication` | [INV-WR2](04-analysis-writing.md#inv-wr2-ללא-כפילות-בלוק-י-מפנה-לא-חוזר) | מילה-במילה בלבד — לא תופס כפילות מנוסחת-מחדש |
| `claims_coverage` | [INV-WR3](04-analysis-writing.md#inv-wr3-מענה-לכל-טענה-של-הצד-המפסיד) | סמנטי (Claude), סובלני ל-20% חוסר |
ראה [INV-QA4](#inv-qa4-שערי-התוכן-האוטומטיים-אוכפים-את-wr1wr3-content--g11). WR4 (טענות
מקוריות) ו-WR5 ("מבחן-השופט") **אינם** מכוסים על-ידי `validate_decision` — WR4 נאכף
בנקודת-החילוץ (`extract_claims`), WR5 הוא שער-איכות אנושי/agent. הסוכן `legal-qa`
(ראה [X4-agents.md](X4-agents.md)) מוסיף שערים ידניים מעבר ל-6 הקוד-יים (קול-דפנה,
שאילתות-קורפוס, צירוף-פסיקה) — `.claude/agents/legal-qa.md`.
---
## 2. שערים אנושיים — היו"ר מכריעה
המערכת מסייעת; ההכרעה היא של היו"ר. שלושה שערים אנושיים מובנים בקוד-הזרימה ואינם ניתנים
לעקיפה אוטומטית (זהו [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)).
### 2.1 אישור הלכה (halacha approval)
הלכות מחולצות אוטומטית מפסיקה (`halacha_extractor.py`), אך **נכנסות כ-`pending_review`
ובלתי-נראות לחיפוש** עד אישור היו"ר:
- **כתיבה:** `db.add_halacha` קובע `review_status = "approved" if auto_approve else
"pending_review"` (`db.py:3003`), כאשר `auto_approve` נגזר מסף-ביטחון
`HALACHA_AUTO_APPROVE_THRESHOLD` (ברירת-מחדל `0.80`, `config.py:111`). הלכות מתחת לסף
נשארות `pending_review`.
- **שער-האישור:** `halacha_review(halacha_id, status, reviewer="דפנה", …)`
(`tools/precedent_library.py:291`, נחשף ב-`server.py:298`) — היו"ר מאשרת/דוחה/עורכת.
`status ∈ {pending_review, approved, rejected, published}` (`precedent_library.py:311`).
- **תור-ההמתנה:** `halachot_pending(limit=100)` (`precedent_library.py:335`) מחזיר את
`review_status='pending_review'`.
- **חשיפה רק לאחר אישור:** החיפוש מסנן `h.review_status IN ('approved','published')`
(`db.py:3168` ו-`db.py:3401`) — הלכה שלא אושרה **לעולם** לא עולה בתוצאות.
### 2.2 בחירת תוצאה (outcome selection)
`set_outcome(case_number, outcome, reasoning="")` (`tools/workflow.py:145`,
`server.py:646`) — היו"ר קובעת `outcome ∈ {rejected, accepted, partial}`
(`workflow.py:163`). זוהי **הכרעה משפטית**: היא קודמת לכתיבת-הטיוטה וקובעת את מסלול-הדיון
(ראה [04-analysis-writing.md](04-analysis-writing.md) §3). אין נתיב שבו המערכת בוחרת תוצאה
לבד — אם לא סופק נימוק, המערכת מציעה כיווני-נימוק (`brainstorm`), אך הבחירה נשארת אנושית.
### 2.3 פידבק היו"ר (chair feedback)
- `record_chair_feedback(case_number, feedback_text, block_id, category, …)`
(`tools/workflow.py:348`, `server.py:896`) — מתעד הערת-דפנה; `category` מתוך
`{missing_content, wrong_tone, wrong_structure, factual_error, style, other}`
(`workflow.py:367`).
- `list_chair_feedback(case_number, category, unresolved_only=True)`
(`tools/workflow.py:393`, `server.py:910`) — שליפה לסקירה.
הפידבק מזין את לולאת-הלמידה ([07-learning.md](07-learning.md)) ואת
[legal-decision-lessons.md](../legal-decision-lessons.md). זהו שיפוט-אנושי על איכות —
לעולם לא מוסק או מוחל אוטומטית.
---
## 3. Invariants של התחום
### INV-QA1: אישור הלכה הוא שער אנושי (governance →G10)
**כלל:** אישור הלכה הוא **הכרעה ידנית של היו"ר**. הלכות שחולצו אוטומטית הן
`pending_review` עד שהיו"ר מאשרת; **רק הלכות מאושרות** (`approved`/`published`) עולות
בחיפוש. תור-ההמתנה חייב להיות **נראה** (`halachot_pending`) כדי שאישור-חסר לא יישאר סמוי.
**מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* (human-in-the-loop) ·
Council of Europe / CEPEJ (2018, under user control) · Federal Judicial Center —
*Judicial Writing Manual* (2d ed.) | סטטוס: verified
**אכיפה:** ברירת-מחדל `pending_review` בכתיבה (`db.py:3003`) + סינון
`review_status IN ('approved','published')` בכל query (`db.py:3168`, `db.py:3401`) + שער-אישור
`halacha_review` (`precedent_library.py:291`).
**הפרה ידועה:** 10/19 הלכות מאושרות — שער-ידני שקוף בלי נראות-backlog; ההפרש התגלה במקרה →
ממצא ל-[audit](../audit-report.md) (ראה גם [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)).
### INV-QA2: בחירת-תוצאה ופידבק הם שערים אנושיים (governance →G10)
**כלל:** **בחירת התוצאה** (`set_outcome`) ו**פידבק-היו"ר** (`record_chair_feedback`) הם
שערים אנושיים — **לעולם לא אוטומטיים**. המערכת מסייעת (מציעה כיווני-נימוק, מתעדת הערות),
אך ההכרעה והשיפוט-על-האיכות הם של היו"ר.
**מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* ("never replace human
judgment") · Council of Europe / CEPEJ (2018, under user control) · Federal Judicial
Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified
**אכיפה:** `set_outcome` דורש `outcome` מפורש מהיו"ר (`workflow.py:145-165`);
`record_chair_feedback`/`list_chair_feedback` מתעדים בלבד (`workflow.py:348, 393`) — אין
מסלול-קוד שמסיק תוצאה או פידבק לבד.
**הפרה ידועה:** —
### INV-QA3: החלטה לא מיוצאת עם כשל קריטי (governance →G10)
**כלל:** החלטה **אינה ניתנת לייצוא** כל עוד שער-QA **קריטי** נכשל
(`claims_coverage` או `structural_integrity`). `export_blocked` חייב להיבדק לפני ייצוא;
ייצוא בכשל-קריטי הוא הפרה. שערי-`warning` שנכשלים מתועדים אך אינם חוסמים.
**מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* (controlled, auditable
AI output) · Council of Europe / CEPEJ (2018, under user control) · Federal Judicial
Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified
**אכיפה:** `export_blocked = critical_failures > 0` (`qa_validator.py:362`); נאכף בשער-הזרימה
של הסוכן `legal-exporter` ("לעולם אל תייצא בלי `validate_decision` קודם", "בדוק שאין
כשלים קריטיים" — `.claude/agents/legal-exporter.md:71, 149`). קושר ל-[06-export.md](06-export.md).
**הפרה ידועה:** `export_docx` (`drafting.py:384`) **אינו** מריץ `validate_decision` בעצמו —
החסימה היא ברמת-הזרימה/agent, לא hard-block בקוד-הייצוא. פער זה → ראה [§4](#4-current-vs-target--ממצאי-audit) (audit).
### INV-QA4: שערי-התוכן האוטומטיים אוכפים את WR1WR3 (content →G11)
**כלל:** שערי-התוכן האוטומטיים מתפעלים את invariants-התוכן: `neutral_background`↔
[WR1](04-analysis-writing.md#inv-wr1-רקע-ניטרלי-בלוק-ו--עובדות-בלבד) (רקע ניטרלי) ·
`no_duplication`↔[WR2](04-analysis-writing.md#inv-wr2-ללא-כפילות-בלוק-י-מפנה-לא-חוזר)
(ללא כפילות) · `claims_coverage`↔[WR3](04-analysis-writing.md#inv-wr3-מענה-לכל-טענה-של-הצד-המפסיד)
(מענה-לטענות). האכיפה האוטומטית היא **רצפה, לא תקרה** — WR4/WR5 וההבטים העדינים (שיפוט-עקיף,
כפילות מנוסחת-מחדש) נשארים בשיקול-הדעת האנושי (INV-QA1QA3).
**מקור-סמכות:** היו"ר (עו"ד דפנה תמיר) + [04-analysis-writing.md](04-analysis-writing.md)
(INV-WR1WR3) + `mcp-server/src/legal_mcp/services/qa_validator.py` (הבדיקות בפועל).
**אכיפה:** `check_neutral_background` (`qa_validator.py:66`), `check_no_duplication`
(`qa_validator.py:235`), `check_claims_coverage` (`qa_validator.py:107`).
**הפרה ידועה:** —
---
## 4. Current vs Target — ממצאי-audit
- **Halacha backlog בלתי-נראה (INV-QA1):** 10/19 הלכות מאושרות; 9 נשארו `pending_review`
ולא עלו בחיפוש. השער עבד כשורה — אך חוסר-נראות של ה-backlog הסתיר את הפער עד שהתגלה
במקרה. **Target:** מדד-נראות (count `pending_review`) כחלק מבדיקת-בריאות, לא רק
`halachot_pending` בדרישה. ראה [audit](../audit-report.md).
- **שער-ייצוא אכוף-זרימה ולא אכוף-קוד (INV-QA3):** `export_docx` לא קורא ל-`validate_decision`;
החסימה תלויה במשמעת הסוכן `legal-exporter`. **Target:** hard-block בתוך `export_docx`
(בדיקת `qa_results`/`export_blocked` לפני כתיבת DOCX) כדי שלא יהיה ניתן לעקיפה.
---
## 5. הפניות-אחיות
- [00-constitution.md](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) —
INV-G10 (שערים אנושיים) + INV-G11 + הבחנת שתי-הסמכויות.
- [04-analysis-writing.md](04-analysis-writing.md) — INV-WR1WR5 שהשערים האוטומטיים מתפעלים.
- [06-export.md](06-export.md) — ייצוא DOCX (השלב אחרי המעבר בשער הקריטי).
- [07-learning.md](07-learning.md) — לולאת פידבק-היו"ר + Hermes שמעדכנת lessons/SKILL.
- [X4-agents.md](X4-agents.md) — הסוכן `legal-qa` (שערים ידניים נוספים) ו-`legal-exporter`.
- [X5-audit-provenance.md](X5-audit-provenance.md) — audit-trail לפלטי-AI ועקיבוּת-מקור.