feat(digests): use Sonnet for digest metadata extraction (X12)

חילוץ-המטא-דאטה של יומון (תג-מושג, כותרת-הלכה, מראה-מקום, תגיות מסיכום
עמוד-אחד) הוא משימה פשוטה בנפח גבוה — Sonnet הוא נקודת-האיזון מהירות/עלות,
בניגוד לחילוץ-הלכות שמצמיד Opus.

- config.DIGEST_EXTRACT_MODEL (env-tunable, ברירת-מחדל claude-sonnet-4-6).
- digest_metadata_extractor.extract(model=None) → ברירת-מחדל מה-config; קודם
  לא צוין model → רץ על ברירת-המחדל של ה-CLI (Opus 4.8).

אומת: extract על יומון 5163 עם Sonnet החזיר תג-מושג/כותרת/מראה-מקום/תחום/
תגיות תקינים (~36s). claude_session נשאר local-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 19:58:48 +00:00
parent e82eeaad9f
commit e6dc410d7d
2 changed files with 11 additions and 1 deletions

View File

@@ -54,6 +54,10 @@ REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6380/0")
# pinned.
HALACHA_EXTRACT_MODEL = os.environ.get("HALACHA_EXTRACT_MODEL", "claude-opus-4-8")
HALACHA_EXTRACT_EFFORT = os.environ.get("HALACHA_EXTRACT_EFFORT", "xhigh")
# Digest (X12) metadata extraction is a simpler, high-volume task (concept tag,
# headline, underlying citation, tags from a one-page summary) — Sonnet is the
# speed/cost sweet-spot here, unlike halacha extraction which pins Opus. Tune via env.
DIGEST_EXTRACT_MODEL = os.environ.get("DIGEST_EXTRACT_MODEL", "claude-sonnet-4-6")
# Effort for BULK queue-drain extraction (process_pending over many precedents).
# xhigh is the quality sweet-spot for a single precedent but very slow at scale
# (a 64-chunk case ≈ 20 min). Bulk drains use a lighter effort to cut wall-clock;

View File

@@ -19,6 +19,7 @@ from __future__ import annotations
import logging
from datetime import date as date_type
from legal_mcp import config
from legal_mcp.config import parse_llm_json
from legal_mcp.services import claude_session
@@ -79,13 +80,17 @@ def _norm_date(result: dict, key: str) -> date_type | None:
return None
async def extract(raw_text: str) -> dict:
async def extract(raw_text: str, model: str | None = None) -> dict:
"""Extract digest metadata from raw text. Returns a dict (never raises).
Keys: yomon_number, digest_date (date|None), concept_tag, headline_holding,
summary, underlying_citation, underlying_court, underlying_date (date|None),
underlying_judge, practice_area, appeal_subtype, subject_tags (list[str]).
Missing/invalid fields are omitted so the caller's merge keeps user values.
Model: defaults to ``config.DIGEST_EXTRACT_MODEL`` (Sonnet — this is a
high-volume, simple extraction; no need for Opus). Override per-call via
``model``.
"""
text = (raw_text or "").strip()
if not text:
@@ -95,6 +100,7 @@ async def extract(raw_text: str) -> dict:
try:
result = await claude_session.query_json(
user_msg, system=DIGEST_EXTRACTION_PROMPT,
model=(model or config.DIGEST_EXTRACT_MODEL or None),
)
except Exception as e: # surfaced as warning, not swallowed silently (§6)
logger.warning("digest_metadata_extractor: query failed: %s", e)