feat(spec): X11 citation-corroboration + INV-G10 amendment + Opus 4.8 halacha extraction
ספ חדש לשכבת citator פנימית — תיקוף הלכות לפי טיפול-שיפוטי מצטבר (ציטוטים נכנסים), לצמצום היקף האישור-הידני של היו"ר: - docs/spec/X11-citation-corroboration.md — 6 invariants (INV-COR1–COR6), כל אחד עם ≥3 מקורות מקצועיים (Shepard's/KeyCite, Hellyer LLJ 2018, UNC Law, NCSC/JTC, CEPEJ). - docs/spec/00-constitution.md — תיקון מבוקר ל-INV-G10: השער מסופק ע"י טיפול-שיפוטי-מצטבר לתת-הקבוצה החיובית, שער-היו"ר נשאר חובה לזנב ולשלילי. + X11 באינדקס. - Opus 4.8 @ xhigh כמודל חילוץ הלכות (config HALACHA_EXTRACT_MODEL/EFFORT, env-tunable; claude_session model/effort params; halacha_extractor מחווט). מבוסס A/B 2026-05-31: פחות חילוץ-יתר, 100% quote-verified, ביטחון מכויל. - scripts/ab_halacha_opus48.py — harness A/B לא-הרסני להשוואת מודל/effort בחילוץ הלכות. - .taskmaster #70 (FU-2c-b) — תיעוד dedup שפר + סריקת-קורפוס (0 stubs תקועים נותרו). תנאי-קדם (זהות נקייה) הושלם: שפר מוזג לרשומה קנונית + סריקת 128 רשומות. audit-findings גלויים ב-X11 §7: קישור הלכה↔ציטוט + סיווג-טיפול = greenfield, ל-implementation plan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,19 @@ POSTGRES_URL = os.environ.get(
|
||||
# Redis
|
||||
REDIS_URL = os.environ.get("REDIS_URL", "redis://127.0.0.1:6380/0")
|
||||
|
||||
# Claude CLI — model + effort for halacha extraction.
|
||||
# All LLM calls go through the local `claude -p` CLI (claude_session.py).
|
||||
# By default the CLI uses the developer's session default model with no
|
||||
# explicit effort. For halacha extraction we pin Opus 4.8 @ xhigh: the
|
||||
# 2026-05-31 A/B (scripts/ab_halacha_opus48.py) showed it cuts over-extraction
|
||||
# (~124→51 on שטיין) at 100% quote-verification with honest confidence
|
||||
# calibration. Env-overridable so the model/effort can be tuned without a
|
||||
# code change (set to "" to fall back to the CLI default). Other extractors
|
||||
# (claims, metadata, block-writing, QA) keep the CLI default unless similarly
|
||||
# pinned.
|
||||
HALACHA_EXTRACT_MODEL = os.environ.get("HALACHA_EXTRACT_MODEL", "claude-opus-4-8")
|
||||
HALACHA_EXTRACT_EFFORT = os.environ.get("HALACHA_EXTRACT_EFFORT", "xhigh")
|
||||
|
||||
# Voyage AI
|
||||
VOYAGE_API_KEY = os.environ.get("VOYAGE_API_KEY", "")
|
||||
VOYAGE_MODEL = os.environ.get("VOYAGE_MODEL", "voyage-law-2")
|
||||
|
||||
@@ -47,6 +47,8 @@ async def query(
|
||||
max_turns: int = 1,
|
||||
*,
|
||||
system: str | None = None,
|
||||
model: str | None = None,
|
||||
effort: str | None = None,
|
||||
) -> str:
|
||||
"""Send a prompt to Claude Code headless and return the text response.
|
||||
|
||||
@@ -62,6 +64,13 @@ async def query(
|
||||
CLI doesn't expose API-level caching. The parameter exists so
|
||||
extractors can structure their calls cleanly today, and to make
|
||||
a future SDK-backed path drop-in.
|
||||
model: Optional model alias/id (e.g. ``claude-opus-4-8``). When set,
|
||||
passed as ``--model``; otherwise the CLI's session default is
|
||||
used. Lets quality-sensitive extractors (halacha) pin a stronger
|
||||
model without changing the default for every caller.
|
||||
effort: Optional effort level (``low``/``medium``/``high``/``xhigh``/
|
||||
``max``). When set, passed as ``--effort``. Pairs with ``model``;
|
||||
an empty string is treated as "unset" (CLI default).
|
||||
|
||||
Returns:
|
||||
The text response from Claude.
|
||||
@@ -80,6 +89,10 @@ async def query(
|
||||
"--output-format", "json",
|
||||
"--max-turns", str(max_turns),
|
||||
]
|
||||
if model:
|
||||
cmd += ["--model", model]
|
||||
if effort:
|
||||
cmd += ["--effort", effort]
|
||||
|
||||
try:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
@@ -135,12 +148,15 @@ async def query_json(
|
||||
timeout: int = DEFAULT_TIMEOUT,
|
||||
*,
|
||||
system: str | None = None,
|
||||
model: str | None = None,
|
||||
effort: str | None = None,
|
||||
) -> dict | list | None:
|
||||
"""Send a prompt and parse the response as JSON.
|
||||
|
||||
Uses parse_llm_json for robust parsing (handles markdown wrapping, truncation).
|
||||
``model``/``effort`` are forwarded to :func:`query` (see its docstring).
|
||||
"""
|
||||
raw = await query(prompt, timeout=timeout, system=system)
|
||||
raw = await query(prompt, timeout=timeout, system=system, model=model, effort=effort)
|
||||
return parse_llm_json(raw)
|
||||
|
||||
|
||||
|
||||
@@ -304,7 +304,12 @@ async def _extract_chunk(
|
||||
last_err: Exception | None = None
|
||||
for attempt in range(CHUNK_RETRY_ATTEMPTS + 1):
|
||||
try:
|
||||
result = await claude_session.query_json(user_msg, system=base_prompt)
|
||||
result = await claude_session.query_json(
|
||||
user_msg,
|
||||
system=base_prompt,
|
||||
model=config.HALACHA_EXTRACT_MODEL or None,
|
||||
effort=config.HALACHA_EXTRACT_EFFORT or None,
|
||||
)
|
||||
except Exception as e:
|
||||
last_err = e
|
||||
logger.warning(
|
||||
|
||||
Reference in New Issue
Block a user