feat(style-acq T0): הזרקת פרופיל-הקול לכותב + מדיניות-העתקה + הפרדת דוגמאות↔פסיקה
הלוֹבר הראשי של מערכת רכישת-הסגנון. block_writer עבר היום מ"העתקה + ערבוב-מהות" ל"הכללת-סגנון + הפרדה": - _build_style_context: טוען את daphna-voice-fingerprint.md (פרופיל-הקול המופשט — המנגנון המרכזי) + מדיניות-העתקה מפורשת לפי סוג-תוכן (נוסחה→מותר, ניתוח→הכלל, מהות מתיק אחר→אסור). INV-LRN5. - _build_precedents_context: פוצל לשני זרמים נפרדים — daphna_style_exemplars (איך דפנה כותבת) מול case_law_citations (מהות לציטוט). - block-yod prompt: שני סעיפים מסומנים במקום "פסיקה רלוונטית (צטט מכאן)" שערבב סגנון ומהות; הדוגמאות-סגנוניות מתויגות "מבנה/קול בלבד". INV: G11 (סגנון דפנה), INV-LRN5 (טוהר-הקול). חלק מתוכנית style-acquisition. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -248,8 +248,12 @@ BLOCK_PROMPTS = {
|
|||||||
## חומרי מקור:
|
## חומרי מקור:
|
||||||
{source_context}
|
{source_context}
|
||||||
|
|
||||||
## פסיקה רלוונטית (צטט מכאן ומהידע הכללי שלך):
|
## דוגמאות-סגנון מהחלטות דפנה — מבנה וקול בלבד:
|
||||||
{precedents_context}
|
⚠️ אלה דוגמאות ל**איך** דפנה כותבת (מבנה, קצב, תנועות-הנמקה, ביטויים) — **לא מקור-תוכן**. הכלל המבחין: נוסחה/בוילרפלייט קבוע (פתיח דוקטרינלי, תבנית-סיום) → מותר להעתיק; ניתוח/טענות ספציפיים → **הכלל את הדפוס והתאם לתיק שלפניך**, אל תעתיק; מהות משפטית (הלכה/עובדה) מתיק אחר → **אסור** להעתיק.
|
||||||
|
{daphna_style_exemplars}
|
||||||
|
|
||||||
|
## פסיקה רלוונטית לציטוט (צטט מכאן ומהידע הכללי שלך):
|
||||||
|
{case_law_citations}
|
||||||
|
|
||||||
## סגנון דפנה:
|
## סגנון דפנה:
|
||||||
{style_context}""",
|
{style_context}""",
|
||||||
@@ -327,7 +331,9 @@ async def write_block(
|
|||||||
claims_context = await _build_claims_context(case_id)
|
claims_context = await _build_claims_context(case_id)
|
||||||
direction_context = _build_direction_context(decision)
|
direction_context = _build_direction_context(decision)
|
||||||
plans_context = await _build_plans_context(case_id)
|
plans_context = await _build_plans_context(case_id)
|
||||||
precedents_context, _precedent_case_law_ids = await _build_precedents_context(case_id, block_id)
|
daphna_style_exemplars, case_law_citations, _precedent_case_law_ids = (
|
||||||
|
await _build_precedents_context(case_id, block_id)
|
||||||
|
)
|
||||||
style_context = await _build_style_context()
|
style_context = await _build_style_context()
|
||||||
discussion_context = await _build_previous_blocks_context(case_id, decision)
|
discussion_context = await _build_previous_blocks_context(case_id, decision)
|
||||||
appraiser_facts_context = await _build_appraiser_facts_context(case_id)
|
appraiser_facts_context = await _build_appraiser_facts_context(case_id)
|
||||||
@@ -363,7 +369,8 @@ async def write_block(
|
|||||||
claims_context=claims_context,
|
claims_context=claims_context,
|
||||||
direction_context=direction_context,
|
direction_context=direction_context,
|
||||||
plans_context=plans_context,
|
plans_context=plans_context,
|
||||||
precedents_context=precedents_context,
|
daphna_style_exemplars=daphna_style_exemplars,
|
||||||
|
case_law_citations=case_law_citations,
|
||||||
style_context=style_context,
|
style_context=style_context,
|
||||||
discussion_context=discussion_context,
|
discussion_context=discussion_context,
|
||||||
structure_guidance=structure_guidance,
|
structure_guidance=structure_guidance,
|
||||||
@@ -707,9 +714,16 @@ async def _build_post_hearing_context(case_id: UUID) -> str:
|
|||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
async def _build_precedents_context(case_id: UUID, block_id: str) -> tuple[str, list[str]]:
|
async def _build_precedents_context(
|
||||||
"""Search for similar precedent paragraphs from other decisions and case law."""
|
case_id: UUID, block_id: str,
|
||||||
parts = []
|
) -> tuple[str, str, list[str]]:
|
||||||
|
"""Two SEPARATE streams (INV-LRN5 — keep style apart from substance):
|
||||||
|
1. style_exemplars — Dafna's own block-level paragraphs (HOW she writes; structure/voice).
|
||||||
|
2. case_law_citations — precedent case-law (substantive material to quote).
|
||||||
|
Returns (style_exemplars, case_law_citations, case_law_ids).
|
||||||
|
"""
|
||||||
|
style_parts: list[str] = []
|
||||||
|
caselaw_parts: list[str] = []
|
||||||
case_law_ids: list[str] = []
|
case_law_ids: list[str] = []
|
||||||
try:
|
try:
|
||||||
case = await db.get_case(case_id)
|
case = await db.get_case(case_id)
|
||||||
@@ -718,19 +732,18 @@ async def _build_precedents_context(case_id: UUID, block_id: str) -> tuple[str,
|
|||||||
query = f"דיון משפטי בנושא {subject}" if subject else "דיון משפטי ועדת ערר"
|
query = f"דיון משפטי בנושא {subject}" if subject else "דיון משפטי ועדת ערר"
|
||||||
query_emb = await embeddings.embed_query(query)
|
query_emb = await embeddings.embed_query(query)
|
||||||
|
|
||||||
# Search 1: paragraph_embeddings (from other decisions by Dafna)
|
# Stream 1: paragraph_embeddings — Dafna's own prose (STYLE exemplars, not content)
|
||||||
para_results = await db.search_similar_paragraphs(
|
para_results = await db.search_similar_paragraphs(
|
||||||
query_embedding=query_emb, limit=10, block_type="block-yod",
|
query_embedding=query_emb, limit=10, block_type="block-yod",
|
||||||
)
|
)
|
||||||
# Filter out same case
|
|
||||||
para_results = [r for r in para_results if r.get("case_number", "") != case_number]
|
para_results = [r for r in para_results if r.get("case_number", "") != case_number]
|
||||||
for r in para_results[:4]:
|
for r in para_results[:4]:
|
||||||
parts.append(
|
style_parts.append(
|
||||||
f"[החלטת {r.get('case_number', '?')} — {r.get('case_title', '')}, "
|
f"[דוגמת-סגנון — החלטת {r.get('case_number', '?')} "
|
||||||
f"בלוק {r.get('block_type', '')}]\n{r['content'][:500]}"
|
f"{r.get('case_title', '')}, בלוק {r.get('block_type', '')}]\n{r['content'][:500]}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Search 2: case_law_embeddings (precedent case law)
|
# Stream 2: case_law_embeddings — substantive precedent (citations)
|
||||||
pool = await db.get_pool()
|
pool = await db.get_pool()
|
||||||
async with pool.acquire() as conn:
|
async with pool.acquire() as conn:
|
||||||
caselaw_rows = await conn.fetch(
|
caselaw_rows = await conn.fetch(
|
||||||
@@ -746,7 +759,7 @@ async def _build_precedents_context(case_id: UUID, block_id: str) -> tuple[str,
|
|||||||
case_law_ids.append(str(r["id"]))
|
case_law_ids.append(str(r["id"]))
|
||||||
text = r["key_quote"] or r["summary"] or ""
|
text = r["key_quote"] or r["summary"] or ""
|
||||||
if text:
|
if text:
|
||||||
parts.append(
|
caselaw_parts.append(
|
||||||
f"[פסיקה: {r['case_number']} {r['case_name']} ({r.get('court', '')})] "
|
f"[פסיקה: {r['case_number']} {r['case_name']} ({r.get('court', '')})] "
|
||||||
f"score={r['score']:.3f}\n{text[:400]}"
|
f"score={r['score']:.3f}\n{text[:400]}"
|
||||||
)
|
)
|
||||||
@@ -754,16 +767,60 @@ async def _build_precedents_context(case_id: UUID, block_id: str) -> tuple[str,
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Failed to fetch precedents: %s", e)
|
logger.warning("Failed to fetch precedents: %s", e)
|
||||||
|
|
||||||
return ("\n\n".join(parts) if parts else "(אין תקדימים)"), case_law_ids
|
return (
|
||||||
|
"\n\n".join(style_parts) if style_parts else "(אין דוגמאות-סגנון)",
|
||||||
|
"\n\n".join(caselaw_parts) if caselaw_parts else "(אין פסיקה רלוונטית)",
|
||||||
|
case_law_ids,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Cache for the abstract voice profile (read once per process).
|
||||||
|
_VOICE_FINGERPRINT_CACHE: str | None = None
|
||||||
|
|
||||||
|
# Style-acquisition policy (INV-LRN5): how to USE the style material below.
|
||||||
|
_COPY_POLICY = """## מדיניות-סגנון (איך להשתמש בחומר שלהלן) — חובה:
|
||||||
|
**היעד: לכתוב בקול ובשיטה של דפנה — לא להעתיק.** הפרופיל שלהלן הוא ההכללה של *איך* דפנה כותבת; הַחֵל אותו על העובדות של התיק שלפניך.
|
||||||
|
- **תוכן קבוע/נוסחאי** (פתיח דוקטרינלי, תבנית-סיום, ביטויי-מעבר) → מותר להשתמש כלשונו.
|
||||||
|
- **ניתוח/טענות ספציפיים** → הכלל את הדפוס והתאם לתיק; אל תעתיק ניסוח מתיק אחר.
|
||||||
|
- **מהות משפטית (הלכה/עובדה/תקדים) מתיק אחר** → אסור לגרור לכאן; המהות באה מחומרי-המקור והפסיקה של *התיק הזה* בלבד.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _load_voice_fingerprint() -> str:
|
||||||
|
"""Load the abstract authorial-style profile (daphna-voice-fingerprint.md).
|
||||||
|
|
||||||
|
This is the PRIMARY style channel (Authorial Style Profiling): the generalized
|
||||||
|
'how Dafna writes', injected so the writer adapts it rather than copying exemplars.
|
||||||
|
Read-only consumption of a learning artifact (Writing↔Acquisition separation).
|
||||||
|
"""
|
||||||
|
global _VOICE_FINGERPRINT_CACHE
|
||||||
|
if _VOICE_FINGERPRINT_CACHE is not None:
|
||||||
|
return _VOICE_FINGERPRINT_CACHE
|
||||||
|
try:
|
||||||
|
path = config.DATA_DIR.parent / "docs" / "daphna-voice-fingerprint.md"
|
||||||
|
_VOICE_FINGERPRINT_CACHE = path.read_text(encoding="utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("voice-fingerprint not loaded: %s", e)
|
||||||
|
_VOICE_FINGERPRINT_CACHE = ""
|
||||||
|
return _VOICE_FINGERPRINT_CACHE
|
||||||
|
|
||||||
|
|
||||||
async def _build_style_context() -> str:
|
async def _build_style_context() -> str:
|
||||||
"""Build comprehensive style guide from DB patterns + SKILL.md rules.
|
"""Build comprehensive style guide: abstract voice profile (primary) +
|
||||||
|
SKILL.md rules + DB patterns.
|
||||||
|
|
||||||
Per Anthropic: explicit style instructions reduce generic output.
|
Per Anthropic: explicit style instructions reduce generic output. The voice
|
||||||
|
fingerprint is the primary abstract-profile channel (T0 / INV-LRN4-5).
|
||||||
"""
|
"""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
|
# Copy-policy first, then the abstract voice profile (the PRIMARY channel).
|
||||||
|
lines.append(_COPY_POLICY)
|
||||||
|
fingerprint = _load_voice_fingerprint()
|
||||||
|
if fingerprint:
|
||||||
|
lines.append("## פרופיל-הקול של דפנה (טביעת-אצבע — המנגנון המרכזי):\n")
|
||||||
|
lines.append(fingerprint)
|
||||||
|
|
||||||
# Core style rules (from SKILL.md analysis)
|
# Core style rules (from SKILL.md analysis)
|
||||||
lines.append("""## כללי סגנון דפנה תמיר — חובה:
|
lines.append("""## כללי סגנון דפנה תמיר — חובה:
|
||||||
|
|
||||||
@@ -882,7 +939,9 @@ async def get_block_context(case_id: UUID, block_id: str, instructions: str = ""
|
|||||||
claims_context = await _build_claims_context(case_id)
|
claims_context = await _build_claims_context(case_id)
|
||||||
direction_context = _build_direction_context(decision)
|
direction_context = _build_direction_context(decision)
|
||||||
plans_context = await _build_plans_context(case_id)
|
plans_context = await _build_plans_context(case_id)
|
||||||
precedents_context, _ = await _build_precedents_context(case_id, block_id)
|
daphna_style_exemplars, case_law_citations, _ = (
|
||||||
|
await _build_precedents_context(case_id, block_id)
|
||||||
|
)
|
||||||
style_context = await _build_style_context()
|
style_context = await _build_style_context()
|
||||||
discussion_context = await _build_previous_blocks_context(case_id, decision)
|
discussion_context = await _build_previous_blocks_context(case_id, decision)
|
||||||
appraiser_facts_context = await _build_appraiser_facts_context(case_id)
|
appraiser_facts_context = await _build_appraiser_facts_context(case_id)
|
||||||
@@ -914,7 +973,8 @@ async def get_block_context(case_id: UUID, block_id: str, instructions: str = ""
|
|||||||
claims_context=claims_context,
|
claims_context=claims_context,
|
||||||
direction_context=direction_context,
|
direction_context=direction_context,
|
||||||
plans_context=plans_context,
|
plans_context=plans_context,
|
||||||
precedents_context=precedents_context,
|
daphna_style_exemplars=daphna_style_exemplars,
|
||||||
|
case_law_citations=case_law_citations,
|
||||||
style_context=style_context,
|
style_context=style_context,
|
||||||
discussion_context=discussion_context,
|
discussion_context=discussion_context,
|
||||||
structure_guidance=structure_guidance,
|
structure_guidance=structure_guidance,
|
||||||
@@ -942,7 +1002,8 @@ async def get_block_context(case_id: UUID, block_id: str, instructions: str = ""
|
|||||||
"source_documents": source_context,
|
"source_documents": source_context,
|
||||||
"claims": claims_context,
|
"claims": claims_context,
|
||||||
"direction": direction_context,
|
"direction": direction_context,
|
||||||
"precedents": precedents_context,
|
"precedents": case_law_citations,
|
||||||
|
"style_exemplars": daphna_style_exemplars,
|
||||||
"style_guide": style_context,
|
"style_guide": style_context,
|
||||||
"previous_blocks": discussion_context,
|
"previous_blocks": discussion_context,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user