fix(learning): honest 3-channel curator card + capture curator findings (source='curator')
תיקוני-מבנה ללולאת-הלמידה (TaskMaster #157): 1. כרטיס ה-curator (/training טאב "אוצֵר") ספר decision_lessons WHERE source='curator' — ערך שאף מסלול-קוד לא כתב → תמיד 0. עוצב-מחדש (דרך שער-העיצוב Claude Design, אושר) להציג ביושר את שלושת ערוצי-ההזנה לכותב: דיסטילציה→appeal_type_rules (180, זורם), פאנל→decision_lessons (81, ממתין), אוצֵר→source='curator'. get_curator_stats שוכתב. 2. drift ספ↔סוכן: 07-learning.md §1.1 + INV-LRN3 קבעו שהאוצֵר רושם ממצאים כ-decision_lesson source='curator', אך הסוכן כתב comments בלבד — הממצאים האיכותיים אבדו. נוסף כלי-MCP record_curator_findings + §A.5b ב-hermes-curator.md (read-only נשמר; הצעה מגודרת-שער). 3. get_recent_decision_lessons(limit=15) חתך בשקט — נוסף WARN על מה שנחתך (חוקה §6); הפתרון האמיתי = סינתזת-לקחים (TaskMaster #158). Invariants: מקיים INV-LRN1/G10 (שער-יו"ר), INV-LRN3 (לכידה מובנית), INV-IA2/IA5 (מקור-אמת יחיד), G12 (leak-guard עובר), G2 (אין מסלול מקביל — אותם מאגרים). פער-מימוש פתוח מתועד: §A לא רץ אוטומטית על mark-final (pipeline-wake exits) → נדחה לתכנון נפרד. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -403,6 +403,78 @@ async def record_chair_feedback(
|
||||
}, message=f"הערה נרשמה בהצלחה. קטגוריה: {category}.")
|
||||
|
||||
|
||||
_CURATOR_FINDING_CATEGORIES = {"style", "structure", "lexicon", "tabular", "general"}
|
||||
# Agent tags ([סגנון]/[מבנה]/[לקסיקון משפטי]/[טבלאי]) → decision_lessons.category
|
||||
_CURATOR_TAG_TO_CATEGORY = {
|
||||
"סגנון": "style", "מבנה": "structure",
|
||||
"לקסיקון משפטי": "lexicon", "לקסיקון": "lexicon", "טבלאי": "tabular",
|
||||
}
|
||||
|
||||
|
||||
async def record_curator_findings(case_number: str, findings: list[dict]) -> str:
|
||||
"""לכידת ממצאי-האוצֵר כ-decision_lessons מובְנים (source='curator', proposed) — INV-LRN3.
|
||||
|
||||
האוצֵר מזהה דפוסי-סגנון בקריאת הסופי; עד כה הם חיו רק כהערה ארעית בערוץ-התגובות
|
||||
(אובד, לא נסקר). כאן הם נתפסים מבנית כך שיופיעו בטאב ״אוצֵר״ ויעברו שער-יו"ר (INV-LRN1/G10).
|
||||
האוצֵר נשאר read-only על התוכן — הרישום הוא הצעה הממתינה לאישור, לא שינוי-קול.
|
||||
|
||||
Args:
|
||||
case_number: מספר התיק הסופי (= decision_number בקורפוס-הסגנון).
|
||||
findings: רשימת ממצאים, כל אחד {"text": "...", "category"/"tag": "..."}.
|
||||
category ∈ style/structure/lexicon/tabular/general (או tag עברי).
|
||||
"""
|
||||
if not findings:
|
||||
return err("findings ריק — אין ממצאים לרשום.")
|
||||
corpus_id = await db.get_style_corpus_id_by_decision(case_number)
|
||||
if not corpus_id:
|
||||
return err(
|
||||
f"לא נמצאה רשומת style_corpus ל-{case_number} — ודא שהסופי נקלט לקורפוס-הסגנון "
|
||||
"(enroll_style_corpus) לפני רישום ממצאים."
|
||||
)
|
||||
# Dedup against lessons already on this corpus (any source) — re-running §A
|
||||
# must not pile duplicates (INV-LRN3 reliability).
|
||||
existing = {(_norm(r["lesson_text"])) for r in await db.list_decision_lessons(corpus_id)}
|
||||
written, skipped_dup, skipped_empty = [], 0, 0
|
||||
for f in findings:
|
||||
text = (f.get("text") or "").strip()
|
||||
if not text:
|
||||
skipped_empty += 1
|
||||
continue
|
||||
if _norm(text) in existing:
|
||||
skipped_dup += 1
|
||||
continue
|
||||
raw_cat = (f.get("category") or f.get("tag") or "general").strip()
|
||||
category = _CURATOR_TAG_TO_CATEGORY.get(raw_cat, raw_cat)
|
||||
if category not in _CURATOR_FINDING_CATEGORIES:
|
||||
category = "general"
|
||||
row = await db.add_decision_lesson(
|
||||
corpus_id,
|
||||
lesson_text=text,
|
||||
category=category,
|
||||
source="curator",
|
||||
created_by="curator",
|
||||
review_status="proposed",
|
||||
)
|
||||
if row:
|
||||
written.append(str(row["id"]))
|
||||
existing.add(_norm(text))
|
||||
return ok({
|
||||
"corpus_id": str(corpus_id),
|
||||
"written": len(written),
|
||||
"skipped_duplicate": skipped_dup,
|
||||
"skipped_empty": skipped_empty,
|
||||
"lesson_ids": written,
|
||||
}, message=(
|
||||
f"נרשמו {len(written)} ממצאי-אוצֵר (source=curator, ממתינים לשער-יו\"ר ב-/training). "
|
||||
f"{skipped_dup} כפילויות דולגו."
|
||||
))
|
||||
|
||||
|
||||
def _norm(s: str) -> str:
|
||||
"""Normalize lesson text for dedup — collapse whitespace, strip."""
|
||||
return " ".join((s or "").split())
|
||||
|
||||
|
||||
async def list_chair_feedback(
|
||||
case_number: str = "",
|
||||
category: str = "",
|
||||
|
||||
Reference in New Issue
Block a user