feat(learning): lesson synthesis — merge overlapping lessons into richer super-lessons (#158 / INV-LRN8)
הפתרון-האמיתי לחיתוך-השקט limit=15 (#157): במקום ערימת לקחים גולמיים חופפים, ממזגים לקחי-סגנון דומים ל"לקח-על" אחד עשיר ומוכלל — הסט שזורם לכותב קטֵן ומשתבח (Authorial Style Profiling). מחקה את סינתזת-הקנוני (V41/INV-LRN6) על אותה טבלה (G2, אין מאגר-מקביל). מנגנון (services/lesson_synthesis.py, מסלול-יחיד): - אשכול greedy לפי cosine (LESSON_SYNTH_CLUSTER_THRESHOLD) בתוך shard practice_area+category. - מיזוג ע"י claude_session מעוגן-מקור (INV-AH, סגנון-בלבד INV-LRN5, abstain) + שער-drift (cosine מול centroid ≥ LESSON_SYNTH_DRIFT_FLOOR). - לקח-על נכתב source='synthesis' + synthesized_from; המקורות→review_status='superseded'. - שער מדורג-הפיך (הכרעת-יו"ר): מקורות approved → לקח-על approved (זורם), veto-יו"ר משחזר את המקורות (db.revert_lesson_synthesis, מחובר ל-PATCH lessons). - idempotency: lookup-cosine מול synthesis קיים לפני INSERT. נגזרות: SCHEMA_V46 (embedding vector(1024) + synthesized_from + ivfflat); כלי-MCP lesson_synthesize_pending; scripts/backfill_lesson_synthesis.py (--dry-run/--apply, audit CSV); config LESSON_SYNTH_*; spec INV-LRN8; SCRIPTS.md. get_recent_decision_lessons ללא שינוי — superseded יוצא (מסנן approved), synthesis נכנס. UI badges (synthesis/superseded) נדחים לשער-העיצוב (מוגנים ב-fallback, ללא קריסה). בדיקות: py_compile ✓ · leak-guard G12 ✓ · smoke-test טהור לאשכול/cosine/centroid ✓. אימות functional מלא (dry-run מול DB+voyage+claude CLI) — בהוסט אחרי-deploy, כמו V41. Invariants: G2 (מסלול-יחיד, אותה טבלה), INV-AH (עיגון+drift), INV-LRN1/G10 (שער מדורג-הפיך), INV-LRN5 (סגנון-בלבד), INV-LRN8 (חדש). depends-on #157/#159. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -497,6 +497,33 @@ def _norm(s: str) -> str:
|
||||
return " ".join((s or "").split())
|
||||
|
||||
|
||||
async def lesson_synthesize_pending(
|
||||
practice_area: str = "", category: str = "", apply: bool = True,
|
||||
) -> str:
|
||||
"""סינתזת-לקחים (#158 / INV-LRN8): ממזגת לקחי-סגנון חופפים ל"לקח-על" אחד עשיר ומוכלל,
|
||||
כך שהסט שזורם לכותב קטֵן ומשתבח (התקרה limit=15 מפסיקה לחתוך). מאשכלת לפי דמיון (cosine)
|
||||
בתוך shard של practice_area+category, ומסנתזת מעוגן-מקור (INV-AH) עם שער-drift.
|
||||
|
||||
Args:
|
||||
practice_area: לצמצם ל-shard אחד (ריק = כל התחומים).
|
||||
category: style/structure/lexicon/tabular (ריק = כל הקטגוריות).
|
||||
apply: True = כותב (לקח-על approved + מקורות→superseded); False = dry-run.
|
||||
"""
|
||||
from legal_mcp.services import lesson_synthesis
|
||||
shards = await lesson_synthesis.run_pending(practice_area, category, apply=apply)
|
||||
applied = sum(1 for s in shards for c in s["clusters"] if c.get("applied"))
|
||||
clusters = sum(len(s["clusters"]) for s in shards)
|
||||
return ok({
|
||||
"apply": apply,
|
||||
"shards": shards,
|
||||
"clusters_found": clusters,
|
||||
"synthesized": applied,
|
||||
}, message=(
|
||||
f"סינתזת-לקחים: {clusters} אשכולות ב-{len(shards)} shards · "
|
||||
f"{applied} לקחי-על {'נכתבו (approved)' if apply else 'דמו (dry-run)'}."
|
||||
))
|
||||
|
||||
|
||||
async def list_chair_feedback(
|
||||
case_number: str = "",
|
||||
category: str = "",
|
||||
|
||||
Reference in New Issue
Block a user