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:
@@ -275,6 +275,19 @@ HALACHA_CANONICAL_SYNTH_MODEL = os.environ.get("HALACHA_CANONICAL_SYNTH_MODEL",
|
||||
HALACHA_CANONICAL_SYNTH_EFFORT = os.environ.get("HALACHA_CANONICAL_SYNTH_EFFORT", "high")
|
||||
HALACHA_CANONICAL_SYNTH_DRIFT_FLOOR = float(os.environ.get("HALACHA_CANONICAL_SYNTH_DRIFT_FLOOR", "0.80"))
|
||||
|
||||
# Lesson synthesis (#158 / INV-LRN8) — mirrors the canonical-halacha synthesis above
|
||||
# for decision_lessons: cluster overlapping style lessons (cosine ≥ CLUSTER_THRESHOLD,
|
||||
# within a practice_area+category shard) and merge each cluster into one richer
|
||||
# "super-lesson" via a local claude_session pass, grounded in the source lessons
|
||||
# (INV-AH) with a re-embedding DRIFT_FLOOR guard. Opus by default — chair-facing
|
||||
# quality. The synthesised set is smaller, so the writer's limit=15 stops truncating
|
||||
# (the real fix for the silent cap, #157).
|
||||
LESSON_SYNTH_MODEL = os.environ.get("LESSON_SYNTH_MODEL", HALACHA_EXTRACT_MODEL)
|
||||
LESSON_SYNTH_EFFORT = os.environ.get("LESSON_SYNTH_EFFORT", "high")
|
||||
LESSON_SYNTH_DRIFT_FLOOR = float(os.environ.get("LESSON_SYNTH_DRIFT_FLOOR", "0.80"))
|
||||
# Cosine floor for two lessons to land in the same cluster (greedy, within shard).
|
||||
LESSON_SYNTH_CLUSTER_THRESHOLD = float(os.environ.get("LESSON_SYNTH_CLUSTER_THRESHOLD", "0.82"))
|
||||
|
||||
# Mistral OCR (fallback for scanned PDFs — replaces Google Cloud Vision)
|
||||
MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY", "")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user